C语言- 基础数据结构和算法 - 循环链表
作者:互联网
听黑马程序员教程《基础数据结构和算法 (C版本)》,照着老师所讲抄的,
视频地址https://www.bilibili.com/video/BV1vE411f7Jh?p=1
喜欢的朋友可以去看看,欢迎大家一起交流学习。
CircleLinkList.h
1 #ifndef CIRCLELINKLIST 2 #define CIRCLELINKLIST 3 #include<stdio.h> 4 #include<stdlib.h> 5 #include<string.h> 6 7 // 链表小结点 8 typedef struct CIRCLELINKNODE { 9 struct CIRCLELINKNODE* next; 10 }CircleLinkNode; 11 12 // 链表结构体,记录和维护链表状态(头节点的指针,长度多少) 13 typedef struct CIRCLELINKLIST { 14 CircleLinkNode head; // 头节点 15 int size; // 记录链表长度,免得每次要获取链表长度时都要for遍列一次。 16 }CircleLinkList; 17 18 /////////////////////// 针对链表结构体操作的API函数 /////////////////////////////// 19 20 // 定义2个宏,模拟bool 21 #define CIRCLELINKLIST_TRUE 1 22 #define CIRCLELINKLIST_FALSE 0 23 24 // 判断两个值是否相等的函数回调,用于根据值删除,根据值查找 25 typedef int(*COMPARENODE)(CircleLinkNode*, CircleLinkNode*); 26 27 // 打印回调 28 typedef void(*PRINTNODE)(CircleLinkNode*); 29 30 // 初始化链表 31 CircleLinkList* Init_CircleLinkList(); 32 33 // 插入结点 (必城要用 CircleLinkNode* data) 34 void Insert_CircleLinkList(CircleLinkList* clist, int pos, CircleLinkNode* data); 35 36 // 获得第一个元素 37 CircleLinkNode* Front_CircleLinkList(CircleLinkList* clist); 38 39 // 根据位置删除 40 void RemoveByPos_CircleLinkList(CircleLinkList* clist, int pos); 41 42 // 根据值删除(保存的都是地址,不能用地址去比较,,,,通过回调方式交给用户去做) 43 void RemoveByValue_CircleLinkList(CircleLinkList* clist, CircleLinkNode* data,COMPARENODE compare); 44 45 // 获得链表的长度 46 int Size_CircleLinkList(CircleLinkList* clist); 47 48 // 判断是否为空 49 int IsEmpty_CircleLinkList(CircleLinkList* clist); 50 51 // 根据值查找,返回位置 52 int Find_CircleLinkList(CircleLinkList* clist, CircleLinkNode* data, COMPARENODE compare); 53 54 // 打印 55 void Print_CircleLinkList(CircleLinkList* clist, PRINTNODE print); 56 57 // 释放内存 58 void FreeSpace_CircleLinkList(CircleLinkList* clist); 59 #endif // !CIRCLIELINKLIST
CircleLinkList.c
1 #include "CircleLinkList.h" 2 3 /////////////////////// 针对链表结构体操作的API函数 /////////////////////////////// 4 5 // 初始化链表(头节点也一起创建了) 6 CircleLinkList* Init_CircleLinkList() { 7 8 CircleLinkList* clist = (CircleLinkList*)malloc(sizeof(CircleLinkList)); 9 if (clist) { // 先判断是否申请内存成功 10 clist->head.next = &(clist->head); // head.next 指向自己 11 clist->size = 0; 12 } 13 else { 14 clist = NULL; 15 } 16 return clist; 17 } 18 19 // 插入结点 (必城要用 CircleLinkNode* data) 20 void Insert_CircleLinkList(CircleLinkList* clist, int pos, CircleLinkNode* data) { 21 if (clist == NULL) { 22 return; 23 } 24 if (data == NULL) { 25 return; 26 } 27 // 友好判断位置,如果越界,则插入到最后。 28 if (pos<0 || pos> clist->size) { 29 pos = clist->size; 30 } 31 // 根据位置查找插入位置的前一个节点pCurrent(利用辅助指针) 32 CircleLinkNode* pCurrent = &(clist->head); 33 for (int i = 0; i < pos; i++) { 34 pCurrent = pCurrent->next; 35 } 36 //CircleLinkNode* newNode = (CircleLinkNode*)malloc(sizeof(CircleLinkNode)); 37 // 如果是一般链表,则要newNode申请内存, 38 // newNode->next = pCurrent->next; 39 // pCurrent->next = newNode; 40 // 企业链表不用为新节点申请内存, 41 // 因为传进来的参数 data 就是 CircleLinkNode*类型,即指针类型 42 // 所以下面可以直接data->next 43 data->next = pCurrent->next; 44 pCurrent->next = data; 45 46 clist->size++; 47 } 48 49 // 获得第一个元素 50 CircleLinkNode* Front_CircleLinkList(CircleLinkList* clist) { 51 // 第一个元素就是头节点后面的那个元素,,,直接返回。 52 return clist->head.next; 53 } 54 55 // 根据位置删除 56 void RemoveByPos_CircleLinkList(CircleLinkList* clist, int pos) { 57 if (clist == NULL) { 58 return; 59 } 60 if (pos < 0 || pos >= clist->size) { 61 return; // 位置无效(越界)则直接返回。 62 } 63 // 根据位置查找删除位置的前一个节点pCurrent(利用辅助指针) 64 CircleLinkNode* pCurrent = &(clist->head); 65 for (int i = 0; i < pos; i++) { 66 pCurrent = pCurrent->next; 67 } 68 // 缓存当前节点的下一个节点。 69 //CircleLinkNode* delNode = pCurrent->next; 70 //pCurrent->next = delNode->next; 71 pCurrent->next = pCurrent->next->next; 72 73 clist->size--; 74 } 75 76 // 根据值删除(保存的都是地址,不能用地址去比较,,,,通过回调方式交给用户去做) 77 void RemoveByValue_CircleLinkList(CircleLinkList* clist, CircleLinkNode* delData, COMPARENODE compare) { 78 if (clist == NULL) { 79 return; 80 } 81 if (delData == NULL) { 82 return; 83 } 84 // 这个是循环链表 85 // 当前节点(要删除节点)的前一个节点 86 CircleLinkNode* pPrev = &(clist->head); // 这个是要删除的节点的前一个节点 87 CircleLinkNode* pCurrent = pPrev->next; // 这个是要删除的节点 88 int i; 89 for (i = 0; i < clist->size; i++) { 90 if (compare(pCurrent, delData) == CIRCLELINKLIST_TRUE) { 91 pPrev->next = pCurrent->next; 92 clist->size--; 93 break; 94 } 95 pPrev = pCurrent; 96 pCurrent = pCurrent->next; 97 } 98 } 99 100 // 获得链表的长度 101 int Size_CircleLinkList(CircleLinkList* clist) { 102 return clist->size; 103 } 104 105 // 判断是否为空 106 int IsEmpty_CircleLinkList(CircleLinkList* clist) { 107 if (clist->size == 0) { 108 return CIRCLELINKLIST_FALSE; 109 } 110 else { 111 return CIRCLELINKLIST_TRUE; 112 } 113 } 114 115 // 根据值查找,返回位置 116 int Find_CircleLinkList(CircleLinkList* clist, CircleLinkNode* data, COMPARENODE compare) { 117 if (clist == NULL) { 118 return -1; 119 } 120 if (data == NULL) { 121 return -1; 122 } 123 CircleLinkNode* pCurrent = clist->head.next; 124 int i; 125 int flag = -1; 126 for (i = 0; i < clist->size; i++) { 127 if (compare(pCurrent, data) == CIRCLELINKLIST_TRUE) { 128 flag = i; 129 break; 130 } 131 pCurrent = pCurrent->next; 132 } 133 134 return flag; 135 } 136 137 // 打印 138 void Print_CircleLinkList(CircleLinkList* clist, PRINTNODE print) { 139 if (clist == NULL) { 140 return; 141 } 142 // 辅助指针 143 CircleLinkNode* pCurrent = clist->head.next; 144 int i; 145 for (i = 0; i < clist->size * 2; i++) { // *2 乘以2,打印两遍 146 if (pCurrent == &(clist->head)) { 147 pCurrent = pCurrent->next; // 如果打印多遍,则会循环到头节点,头节点不用打印,跳过. 148 printf("-----------------------------------------------------------------\n"); 149 } 150 printf("正在打印第:%-2d个结点(%p)\t",i,pCurrent); 151 print(pCurrent); 152 pCurrent = pCurrent->next; 153 } 154 printf("\n"); 155 } 156 157 // 释放内存 158 void FreeSpace_CircleLinkList(CircleLinkList* clist) { 159 if (clist == NULL) { 160 return; 161 } 162 free(clist); 163 164 }
main.c
1 #define _CRT_SECURE_NO_WARNINGS 2 #include<stdio.h> 3 #include<stdlib.h> 4 #include<string.h> 5 #include "CircleLinkList.h" 6 7 8 // 用户数据 9 typedef struct STUDENT { 10 CircleLinkNode node; 11 int id; 12 char name[32]; 13 int age; 14 }student; 15 /* 16 企业链表的巧妙之处: 17 把node放在student结构体的首地址的位置(第一行); 18 这样在初始化一个student结构体后; 19 只需要利用**强制转换**就可以获得结构体的首地址; 20 需要利用元素时,只需要将地址**强制转换**回结构体类型即可. 21 数据转结构体:(CircleLinkNode*)&st1; //st1 为 student 类型,转换时要加 & 取地址. 22 结构体转数据:(student*)data; // data 为 CircleLinkNode* 类型. 23 */ 24 25 // 打印函数 26 void myPrint(CircleLinkNode* data) { 27 student* st = (student*)data; // 强转,把CircleLinkNod强转成student; 28 printf("学号:%d\t姓名:%s\t年龄:%d\n", st->id, st->name, st->age); 29 } 30 // 删除用的比较函数 31 int myCompare(CircleLinkNode* data1, CircleLinkNode* data2) { 32 student* st1 = (student*)data1; // 强转,把CircleLinkNod强转成student; 33 student* st2 = (student*)data2; // 强转,把CircleLinkNod强转成student; 34 if (strcmp(st1->name,st2->name)==0 && st1->id == st2->id&& st1->age == st2->age) { 35 return CIRCLELINKLIST_TRUE; 36 } 37 return CIRCLELINKLIST_FALSE; 38 } 39 /* 40 单向循环链表 41 最后一个节点的next指向头节点, 42 越界问题(判断是否最后一个节点) 43 第一种方式:判断next是不是等于头结点 44 第二种方式:通过size(),从头节点for循环next,i=size后不再往下走,就找到最后一个节点。 45 插入新节点: 46 newNode->next = pCurrent->next; // pCurrent:表示当前节点,newNode插到pCurrent后面。 47 pCurrent->next = newNode; 48 用企业链表方式实现 49 企业链表的优点就是不需要管理每个节点的内存,只需释放自己申请的内存。 50 */ 51 52 53 54 55 56 int main() { 57 printf("好好学习,天天向上~!\t\t\t 04 循环链表20220605\n\n\n"); 58 59 student st1,st2,st3,st4,st5,st6 ; 60 st1.id = 202201; 61 strcpy(st1.name, "宗宗"); 62 st1.age = 19; 63 64 st2.id = 202202; 65 strcpy(st2.name, "艳艳"); 66 st2.age = 20; 67 68 st3.id = 202203; 69 strcpy(st3.name, "发发"); 70 st3.age = 21; 71 72 st4.id = 202204; 73 strcpy(st4.name, "石头"); 74 st4.age = 22; 75 76 st5.id = 202205; 77 strcpy(st5.name, "好好"); 78 st5.age = 23; 79 80 st6.id = 202206; 81 strcpy(st6.name, "学习"); 82 st6.age = 24; 83 84 //student st7 = { 202207,"天天",54 }; // 不能这样写,会出警告:“CIRCLELINKNODE * ”与“int”的间接级别不同 85 86 // 创建链表 87 CircleLinkList* clist = Init_CircleLinkList(); 88 // 插入数据 注意,插入的是数据的地址,要&取地址,并且要强制转换成 (CircleLinkNode*) 89 Insert_CircleLinkList(clist, 0, (CircleLinkNode*)&st1); 90 Insert_CircleLinkList(clist, 1, (CircleLinkNode*)&st2); 91 Insert_CircleLinkList(clist, 2, (CircleLinkNode*)&st3); 92 Insert_CircleLinkList(clist, 0, (CircleLinkNode*)&st4); 93 Insert_CircleLinkList(clist, 0, (CircleLinkNode*)&st5); 94 Insert_CircleLinkList(clist, 0, (CircleLinkNode*)&st6); 95 //Insert_CircleLinkList(clist, 0, (CircleLinkNode*)&st7); // 上面67行写法错误,会出警告. 96 // 打印 97 Print_CircleLinkList(clist, myPrint); 98 /* 99 正在打印第:0 个结点(000000391ABCF5C8) 学号:202206 姓名:学习 年龄:24 100 正在打印第:1 个结点(000000391ABCF578) 学号:202205 姓名:好好 年龄:23 101 正在打印第:2 个结点(000000391ABCF528) 学号:202204 姓名:石头 年龄:22 102 正在打印第:3 个结点(000000391ABCF438) 学号:202201 姓名:宗宗 年龄:19 103 正在打印第:4 个结点(000000391ABCF488) 学号:202202 姓名:艳艳 年龄:20 104 正在打印第:5 个结点(000000391ABCF4D8) 学号:202203 姓名:发发 年龄:21 105 // 根据打印结果计算 ABCF5C8 - ABCF578 = 80个字节的空间 106 */ 107 108 // 删除 109 student stDel; 110 stDel.id=202203; 111 stDel.age = 21; 112 strcpy(stDel.name, "发发"); 113 RemoveByValue_CircleLinkList(clist, (CircleLinkNode*)&stDel,myCompare); 114 // 打印 115 Print_CircleLinkList(clist, myPrint); 116 117 118 119 // 释放内存 120 FreeSpace_CircleLinkList(clist); 121 122 system("pause"); 123 return 0; 124 }
另: 约瑟夫问题-循环链表典型应用
/*
约瑟夫问题-循环链表典型应用:
例题:n个人围成一个圆圈,首先第1个人从1开始一个人一个人顺时针报数,报
到第 m个人,令其出列。然后再从下一个人开始从1顺时针报数,报到第m个人
再令其出列,…,如此下去,求出列顺序。
假设:m=8,n=3
假设这一圈有8个人,1-2-3-4-5-6-7-8,从第1个人开始数,数到3出列,
第1次出列:1-2-*-4-5-6-7-8 3, 然后从出列的下一个人(第4个人)开始数
第2次出列:1-2-*-4-5-*-7-8 6,
第3次出列:*-2-*-4-5-*-7-8 1,
第4次出列:*-2-*-4-*-*-7-8 5,
第5次出列:*-*-*-4-*-*-7-8 2,
第6次出列:*-*-*-4-*-*-7-* 8,
第7次出列:*-*-*-*-*-*-7-* 4,
第8次出列:*-*-*-*-*-*-*-* 7,
*/
main.c(其他CircleLinkList.c和CircleLinkList.h文件与上面和相同)
1 #define _CRT_SECURE_NO_WARNINGS 2 #include<stdio.h> 3 #include<stdlib.h> 4 #include<string.h> 5 #include "CircleLinkList.h" 6 7 /* 8 约瑟夫问题-循环链表典型应用: 9 例题:n个人围成一个圆圈,首先第1个人从1开始一个人一个人顺时针报数,报 10 到第 m个人,令其出列。然后再从下一个人开始从1顺时针报数,报到第m个人 11 再令其出列,…,如此下去,求出列顺序。 12 13 假设:m=8,n=3 14 假设这一圈有8个人,1-2-3-4-5-6-7-8,从第1个人开始数,数到3出列, 15 第1次出列:1-2-*-4-5-6-7-8 3,然后从出列的下一个人(第4个人)开始数 16 第2次出列:1-2-*-4-5-*-7-8 6, 17 第3次出列:*-2-*-4-5-*-7-8 1, 18 第4次出列:*-2-*-4-*-*-7-8 5, 19 第5次出列:*-*-*-4-*-*-7-8 2, 20 第6次出列:*-*-*-4-*-*-7-* 8, 21 第7次出列:*-*-*-*-*-*-7-* 4, 22 第8次出列:*-*-*-*-*-*-*-* 7, 23 24 */ 25 26 #define M 8 27 #define N 3 28 29 typedef struct MYNUM { 30 CircleLinkNode node; 31 int val; 32 }myNum; 33 34 // 打印函数 35 void myPrint(CircleLinkNode* data) { 36 myNum* num = (myNum*)data; // 强转,把CircleLinkNod强转成student; 37 printf("id:%d\n", num->val); 38 } 39 // 删除用的比较函数 40 int myCompare(CircleLinkNode* data1, CircleLinkNode* data2) { 41 myNum* num1 = (myNum*)data1; // 强转,把CircleLinkNod强转成student; 42 myNum* num2 = (myNum*)data2; // 强转,把CircleLinkNod强转成student; 43 if (num1->val==num2->val) { 44 return CIRCLELINKLIST_TRUE; 45 } 46 return CIRCLELINKLIST_FALSE; 47 } 48 int main() { 49 printf("好好学习,天天向上~!\t\t\t 04 循环链表20220605\n\n\n"); 50 51 // 创建循环链表 52 CircleLinkList* clist = Init_CircleLinkList(); 53 54 myNum num[M]; // 用for循环插入数据到链表 55 int i; 56 for (i = 0; i < M; i++) { 57 num[i].val = i + 1; 58 Insert_CircleLinkList(clist, i, (CircleLinkNode*)&num[i]); 59 } 60 61 Print_CircleLinkList(clist, myPrint); 62 /* 63 正在打印第:0 个结点(0000008BE595FB50) id:1 64 正在打印第:1 个结点(0000008BE595FB60) id:2 65 正在打印第:2 个结点(0000008BE595FB70) id:3 66 正在打印第:3 个结点(0000008BE595FB80) id:4 67 正在打印第:4 个结点(0000008BE595FB90) id:5 68 正在打印第:5 个结点(0000008BE595FBA0) id:6 69 正在打印第:6 个结点(0000008BE595FBB0) id:7 70 正在打印第:7 个结点(0000008BE595FBC0) id:8 71 */ 72 73 int index = 1; 74 int ii = 1; 75 CircleLinkNode* pCurrent = clist->head.next; 76 while (Size_CircleLinkList(clist) > 1) { 77 78 if (index == N) { // N 在上面全局变量中定义为3,也就是遇到3就删除 79 myNum* temNum = (myNum*)pCurrent; 80 printf("第%d次删除 %d\n", ii, temNum->val); 81 ii++; 82 83 // 缓存待删除结点的下一个结点 84 CircleLinkNode* pNext = pCurrent->next; 85 // 根据值删除 86 RemoveByValue_CircleLinkList(clist, pCurrent, myCompare); 87 pCurrent = pNext; 88 if (pCurrent == &(clist->head)) { // 跳过头节点 89 pCurrent = pCurrent->next; 90 } 91 index = 1; // 删除后,重新开始数到N(全局变量为3)再接着删除. 92 } 93 pCurrent = pCurrent->next; 94 if (pCurrent == &(clist->head)) { // 跳过头节点 95 pCurrent = pCurrent->next; 96 } 97 index++; 98 99 if (Size_CircleLinkList(clist) == 1) { 100 myNum* temNum = (myNum*)Front_CircleLinkList(clist); 101 printf("第%d次删除 %d\n", ii, temNum->val); 102 } 103 } 104 /* 105 第1次删除 3 106 第2次删除 6 107 第3次删除 1 108 第4次删除 5 109 第5次删除 2 110 第6次删除 8 111 第7次删除 4 112 第8次删除 7 113 */ 114 system("pause"); 115 return 0; 116 }
标签:C语言,int,CircleLinkNode,pCurrent,链表,next,clist,CircleLinkList,数据结构 来源: https://www.cnblogs.com/stou/p/16345260.html