C语言 链表的使用(链表的增删查改,链表逆转,链表排序)
//链表的使用 #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> #include<string.h> //定义链表结构体 struct LinkCode{ int num; char sname[50]; struct LinkCode * linknext; }; //静态链表 void Linkone(){ struct LinkCode n1, n2, n3, n4, n5; struct LinkCode * head = NULL; //初始化变量 n1.num = 1; sprintf(n1.sname, "小明"); n1.linknext = &n2; n2.num = 2; sprintf(n2.sname, "小红"); n2.linknext = &n3; n3.num = 3; sprintf(n3.sname, "小芳"); n3.linknext = &n4; n4.num = 4; sprintf(n4.sname, "小刚"); n4.linknext = &n5; n5.num = 5; sprintf(n5.sname, "小志"); n5.linknext = NULL; printf("\n==========静态链表============\n"); //循环遍历----查询 printf("\n==========查询============\n"); for (head = &n1; head!= NULL; head = head->linknext) { printf("\nnum=%d,sname=%s,linknext=%x", head->num, head->sname, head->linknext); } //增加 printf("\n==========增加============\n"); struct LinkCode n6; n6.num = 6; sprintf(n6.sname, "小雨"); struct LinkCode n7; n7.num = 7; sprintf(n7.sname, "小丽"); //情景1,插入末尾 printf("\n==========增加到末尾============\n"); n5.linknext = &n6; n6.linknext = NULL; //循环遍历----查询 for (head = &n1; head != NULL; head = head->linknext) { printf("\nnum=%d,sname=%s,linknext=%x", head->num, head->sname, head->linknext); } //情景2,插入到中间2后面 printf("\n==========增加到中间============\n"); struct LinkCode *px=NULL; for (head = &n1; head != NULL; head = head->linknext) { //注意for循环的执行顺序 if (head->num==2) { px = head->linknext; head->linknext = &n7; n7.linknext = px; } printf("\nnum=%d,sname=%s,linknext=%x", head->num, head->sname, head->linknext); } //修改 printf("\n==========修改============\n"); //把小红的num修改成99 for (head = &n1; head != NULL; head = head->linknext) { //strcmp()函数 串比较 str1>str2,返回值 > 0;两串相等,返回0 if (!strcmp(head->sname,"小红")) { head->num = 99; } printf("\nnum=%d,sname=%s,linknext=%x", head->num, head->sname, head->linknext); } //删除 printf("\n==========删除============\n"); //删除num是7的节点 for (head = &n1; head != NULL; head = head->linknext) { if (head->num==7) { //找到该节点的地址,再次重新开始循环遍历 px = head; } } for (head = &n1; head != NULL; head = head->linknext) { if (head->linknext==px) { //找到要删除节点的上一个节点 head->linknext = px->linknext; } printf("\nnum=%d,sname=%s,linknext=%x", head->num, head->sname, head->linknext); } printf("\n"); } //动态链表 void LinkTwo(){ struct LinkCode * head, *p1, *p2, *p3, *p4, *p5; head = p1 = p2 = p3 = p4 = p5 = NULL; //先分配内存 p1 = (struct LinkCode *)malloc(sizeof(struct LinkCode)); p2 = (struct LinkCode *)malloc(sizeof(struct LinkCode)); p3 = (struct LinkCode *)malloc(sizeof(struct LinkCode)); p4 = (struct LinkCode *)malloc(sizeof(struct LinkCode)); p5 = (struct LinkCode *)malloc(sizeof(struct LinkCode)); p1->num = 1; sprintf(p1->sname, "小明1"); p1->linknext = p2; p2->num = 2; sprintf(p2->sname, "小红1"); p2->linknext = p3; p3->num = 3; sprintf(p3->sname, "小芳1"); p3->linknext = p4; p4->num = 4; sprintf(p4->sname, "小刚1"); p4->linknext = p5; p5->num = 5; sprintf(p5->sname, "小志1"); p5->linknext = NULL; printf("\n==========查询============\n"); //循环打印 for (head = p1; head != NULL; head=head->linknext) { printf("\nnum=%d,sname=%s,linknext=%x", head->num, head->sname, head->linknext); } //增加 printf("\n==========增加============\n"); struct LinkCode *p6 = (struct LinkCode *)malloc(sizeof(struct LinkCode)); struct LinkCode *p7 = (struct LinkCode *)malloc(sizeof(struct LinkCode)); p6->num = 6; sprintf(p6->sname, "小雨1"); p6->linknext = NULL; p7->num = 7; sprintf(p7->sname, "小丽1"); p7->linknext = NULL; //情景1,在末尾追加 p5->linknext = p6; printf("\n==========增加(在末尾追加)============\n"); //循环打印 for (head = p1; head != NULL; head = head->linknext) { printf("\nnum=%d,sname=%s,linknext=%x", head->num, head->sname, head->linknext); } printf("\n==========增加(在中间追加)============\n"); struct LinkCode *py = NULL; //情景2,在中间追加(追加在小红后面) for (head = p1; head != NULL; head = head->linknext) { if (!strcmp(head->sname, "小红1")) { py = head->linknext; head->linknext = p7; p7->linknext = py; } printf("\nnum=%d,sname=%s,linknext=%x", head->num, head->sname, head->linknext); } //修改 printf("\n==========修改============\n"); p1->num = 99; for (head = p1; head != NULL; head = head->linknext) { printf("\nnum=%d,sname=%s,linknext=%x", head->num, head->sname, head->linknext); } //删除 printf("\n==========删除============\n"); //删除节点num=4的节点 for (head = p1; head != NULL; head = head->linknext) { if (head->num==4) { //找到要删除的节点 py = head; } } for (head = p1; head != NULL; head = head->linknext) { if (head->linknext==py) { head->linknext = py->linknext; free(py); //注意:动态链表的删除和静态链表的删除不同,静态链表创建是在栈中,而动态链表是用malloc()函数创建 //所以动态链表在堆中,想要删除必须程序员手动用free()函数删除 } printf("\nnum=%d,sname=%s,linknext=%x", head->num, head->sname, head->linknext); } printf("\n"); } //链表销毁 void ClearLink(){ printf("\n==========创建链表============\n"); struct LinkCode * head, *p1, *p2, *p3, *p4, *p5; head = p1 = p2 = p3 = p4 = p5 = NULL; //先分配内存 p1 = (struct LinkCode *)malloc(sizeof(struct LinkCode)); p2 = (struct LinkCode *)malloc(sizeof(struct LinkCode)); p3 = (struct LinkCode *)malloc(sizeof(struct LinkCode)); p4 = (struct LinkCode *)malloc(sizeof(struct LinkCode)); p5 = (struct LinkCode *)malloc(sizeof(struct LinkCode)); p1->num = 1; sprintf(p1->sname, "小明1"); p1->linknext = p2; p2->num = 2; sprintf(p2->sname, "小红1"); p2->linknext = p3; p3->num = 3; sprintf(p3->sname, "小芳1"); p3->linknext = p4; p4->num = 4; sprintf(p4->sname, "小刚1"); p4->linknext = p5; p5->num = 5; sprintf(p5->sname, "小志1"); p5->linknext = NULL; printf("\n==========查询============\n"); //循环打印 for (head = p1; head != NULL; head = head->linknext) { printf("\nnum=%d,sname=%s,linknext=%x", head->num, head->sname, head->linknext); } //销毁链表(只要给我一个头结点的地址,我能够删除整个链表) printf("\n==========销毁链表============\n"); //思路:一个个删除,保留第一个,删除第二个,把第三个重新连接成第二个 struct LinkCode *pt = NULL; //循环打印 while (p1->linknext!=NULL){ //获取第二个的地址 pt = p1->linknext; //把第三个的地址放到第一个后面 p1->linknext = p1->linknext->linknext; //删除第二个 free(pt); } //当p1->linknext!=NULL时,表示整个链表只剩下第一个了 //循环打印 for (head = p1; head != NULL; head = head->linknext) { printf("\nnum=%d,sname=%s,linknext=%x", head->num, head->sname, head->linknext); } //此时再删除第一个 free(head); printf("\n头结点的指针地址%x", head); } //链表逆转 void sort(){ printf("\n==========创建链表============\n"); struct LinkCode * head, *p1, *p2, *p3, *p4, *p5; head = p1 = p2 = p3 = p4 = p5 = NULL; //先分配内存 p1 = (struct LinkCode *)malloc(sizeof(struct LinkCode)); p2 = (struct LinkCode *)malloc(sizeof(struct LinkCode)); p3 = (struct LinkCode *)malloc(sizeof(struct LinkCode)); p4 = (struct LinkCode *)malloc(sizeof(struct LinkCode)); p5 = (struct LinkCode *)malloc(sizeof(struct LinkCode)); p1->num = 1; sprintf(p1->sname, "小明1"); p1->linknext = p2; p2->num = 2; sprintf(p2->sname, "小红1"); p2->linknext = p3; p3->num = 3; sprintf(p3->sname, "小芳1"); p3->linknext = p4; p4->num = 4; sprintf(p4->sname, "小刚1"); p4->linknext = p5; p5->num = 5; sprintf(p5->sname, "小志1"); p5->linknext = NULL; printf("\n==========查询============\n"); //循环打印 for (head = p1; head != NULL; head = head->linknext) { printf("\nnum=%d,sname=%s,linknext=%x", head->num, head->sname, head->linknext); } //链表逆转(只给头结点的地址) printf("\n==========链表逆转============\n"); //思路;存储第一个的地址,让第一个的下一个是NULL,第二个的下一个是原来的第一个,第三个下一个是原来的第二个 head = p1; struct LinkCode *px = head;//头结点 struct LinkCode *py = head->linknext;//第二个节点 struct LinkCode *pz = NULL; while (py!=NULL){ pz = py->linknext;//第三个节点 //开始逆转 py->linknext = px;//将第二个节点的下个节点赋值为第一个节点的地址 //开始将所有指针向前移动1位 px = py;//将第一个节点向前移动1位(移动到第二个节点的位置) py = pz;//将第二个节点向前移动1位(移动到第三个节点的位置) //此时再次进入循环 pz = py->linknext; 这会将第三个节点的值变成第四个节点 //当py->linknext==NULL(即pz =NULL)的时候;表明此时py已经是链表最后一个节点了, //那么py->linknext的值就必须是链表倒数第二个的地址,所以还需要再循环1次,给py->linknext赋值, //再次经过py = pz;此时py==NULL; } //这个时候原来的第一个节点的linknext属性的值还是第二个节点的地址,,这是错误的,应该置为空 head->linknext = NULL; //此时px就是原来链表最后一个节点的地址,因为head必须是头结点并且链表已经逆转了, //所以head的值不能是原来的第一个节点,而应该是原来链表最后一个节点的地址 head = px; //循环打印 for (head; head != NULL; head = head->linknext) { printf("\nnum=%d,sname=%s,linknext=%x", head->num, head->sname, head->linknext); } } //链表排序(方法1) //方法1:交换2个元素的位置 //缺点:理解复杂,操作麻烦 //链表的冒泡排序与数组有巨大区别 //数组每个元素都可以单独确定,a[1]就是第二个元素 //但是链表他的每个元素都是由他的上一个元素确定,不存在有多少个元素的概念,只是上一次可以找到下一个 void Sort2(){ printf("\n==========创建链表============\n"); struct LinkCode * head, *p1, *p2, *p3, *p4, *p5; head = p1 = p2 = p3 = p4 = p5 = NULL; //先分配内存 p1 = (struct LinkCode *)malloc(sizeof(struct LinkCode)); p2 = (struct LinkCode *)malloc(sizeof(struct LinkCode)); p3 = (struct LinkCode *)malloc(sizeof(struct LinkCode)); p4 = (struct LinkCode *)malloc(sizeof(struct LinkCode)); p5 = (struct LinkCode *)malloc(sizeof(struct LinkCode)); p1->num = 30; sprintf(p1->sname, "小明1"); p1->linknext = p2; p2->num = 12; sprintf(p2->sname, "小红1"); p2->linknext = p3; p3->num = 9; sprintf(p3->sname, "小芳1"); p3->linknext = p4; p4->num = 4; sprintf(p4->sname, "小刚1"); p4->linknext = p5; p5->num = 2; sprintf(p5->sname, "小志1"); p5->linknext = NULL; printf("\n==========查询============\n"); //循环打印 for (head = p1; head != NULL; head = head->linknext) { printf("\nnum=%d,sname=%s,linknext=%x", head->num, head->sname, head->linknext); } //链表排序(只给头结点的地址)---由小到大 printf("\n==========链表排序============\n"); //思路;定义3个变量,因为链表无法通过当前元素获取上一个元素,所以只能用一个变量将上一个变量的地址记录下来 //px--代表第一个元素,py代表第二个元素,pz代表第三个元素 //参与比较的是py与pz struct LinkCode * px, *py, *pz; //初始化指针,防止后面出错 px = py = pz=NULL; //在比较链表前2个元素的时候,第一个元素没有上一个元素,所以px=py=head px =py= head=p1; //冒泡排序,每循环一次,会将一个最大值移动到末尾,每移动一次,需要移动的元素就会少一个 //一共5个元素,移动4次后,最大的元素都已经放到链表末尾 int index = 4; while (index){//外层循环 //获取第三个元素的值 pz = py->linknext; if (py->num>pz->num) { //如果第一个节点的num比第二个大,则要求交换位置 //1.把第一个节点的下个节点属性赋值为第三个节点的地址 //此时px是第一个元素 px->linknext = pz; //2.把第二个节点的下个节点属性赋值为第四个节点的地址 py->linknext = pz->linknext; //3.把第三个节点的下个节点属性赋值为第二个节点的地址 pz->linknext = py; //第一组交换完成 //4.将所有节点向前移动一位,这里px已经不是第一个元素了,他往前移动1位 //因为要确定头结点的值,所以前2个元素的比较,必须单独提取出来 head = px = pz; } do{ pz = py->linknext; if (py->num>pz->num) { //如果第一个节点比第二个大,则要求交换位置 //1.把第一个节点的下个节点属性赋值为第三个节点的地址 px->linknext = pz; //2.把第二个节点的下个节点属性赋值为第四个节点的地址 py->linknext = pz->linknext; //3.把第三个节点的下个节点属性赋值为第二个节点的地址 pz->linknext = py; //第一组交换完成 //4.将所有节点向前移动一位 px = pz; } else{ //如果大小顺序正确,3个元素都需要再向前移动1位; //把第二个元素赋值给原来第一个元素 px = py; //把第三个元素赋值给原来第二个元素 py = pz; } } while (py->linknext != NULL);//判断第三个元素是否是空 //再次初始化 //因为此时px,py,pz都已经移动到链表末尾 px = py = head; pz = py->linknext; index--; } //循环打印 for (head; head != NULL; head = head->linknext) { printf("\nnum=%d,sname=%s,linknext=%x", head->num, head->sname, head->linknext); } } //链表排序(方法2) //方法1:交换2个元素的内容,不改变链表节点的位置,只是内容的交换 //优点:操作简单,通俗易懂 void Sort3(){ printf("\n==========创建链表============\n"); struct LinkCode * head, *p1, *p2, *p3, *p4, *p5; head = p1 = p2 = p3 = p4 = p5 = NULL; //先分配内存 p1 = (struct LinkCode *)malloc(sizeof(struct LinkCode)); p2 = (struct LinkCode *)malloc(sizeof(struct LinkCode)); p3 = (struct LinkCode *)malloc(sizeof(struct LinkCode)); p4 = (struct LinkCode *)malloc(sizeof(struct LinkCode)); p5 = (struct LinkCode *)malloc(sizeof(struct LinkCode)); p1->num = 3; sprintf(p1->sname, "小明1"); p1->linknext = p2; p2->num = 12; sprintf(p2->sname, "小红1"); p2->linknext = p3; p3->num = 9; sprintf(p3->sname, "小芳1"); p3->linknext = p4; p4->num = 4; sprintf(p4->sname, "小刚1"); p4->linknext = p5; p5->num = 2; sprintf(p5->sname, "小志1"); p5->linknext = NULL; printf("\n==========查询============\n"); //循环打印 for (head = p1; head != NULL; head = head->linknext) { printf("\nnum=%d,sname=%s,linknext=%x", head->num, head->sname, head->linknext); } //链表排序(只给头结点的地址)---由小到大 printf("\n==========链表排序============\n"); struct LinkCode *px = NULL; struct LinkCode *py = NULL; struct LinkCode sz; for (px = head = p1; px != NULL; px = px->linknext) { for (py = head = p1; py != NULL; py = py->linknext) { //双循环 //外循环的一个元素跟其他5个元素相比较,只要px<py就会发生交换 //导致当2是最小元素的时候,他必须先跟前面比他大的所有元素,把自己变成最大,并非单单与最大元素进行交换 //也就是小元素会不断的被推到最前面 if (px->num<py->num) { sz.num = px->num; px->num = py->num; py->num = sz.num; sprintf(sz.sname, px->sname); sprintf(px->sname, py->sname); sprintf(py->sname, sz.sname); } } } //循环打印 for (head = p1; head != NULL; head = head->linknext) { printf("\nnum=%d,sname=%s,linknext=%x", head->num, head->sname, head->linknext); } } void main(){ //Linkone(); //LinkTwo(); //ClearLink(); //sort(); //Sort2(); Sort3(); system("pause"); }