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语言- 基础数据结构和算法 - 循环链表的更多相关文章
- C语言- 基础数据结构和算法 - 09 栈的应用_中缀表达式转后缀表达式20220611
09 栈的应用_中缀表达式转后缀表达式20220611 听黑马程序员教程<基础数据结构和算法 (C版本)>, 照着老师所讲抄的, 视频地址https://www.bilibili.com/ ...
- C语言- 基础数据结构和算法 - 08 栈的应用_就近匹配20220611
听黑马程序员教程<基础数据结构和算法 (C版本)>, 照着老师所讲抄的, 视频地址https://www.bilibili.com/video/BV1vE411f7Jh?p=1 喜欢的朋友 ...
- C语言- 基础数据结构和算法 - 队列的顺序存储
听黑马程序员教程<基础数据结构和算法 (C版本)>, 照着老师所讲抄的, 视频地址https://www.bilibili.com/video/BV1vE411f7Jh?p=1 喜欢的朋友 ...
- C语言- 基础数据结构和算法 - 栈的链式存储
听黑马程序员教程<基础数据结构和算法 (C版本)>, 照着老师所讲抄的, 视频地址https://www.bilibili.com/video/BV1vE411f7Jh?p=1 喜欢的朋友 ...
- C语言- 基础数据结构和算法 - 栈的顺序存储
听黑马程序员教程<基础数据结构和算法 (C版本)>, 照着老师所讲抄的, 视频地址https://www.bilibili.com/video/BV1vE411f7Jh?p=1 喜欢的朋友 ...
- C语言 - 基础数据结构和算法 - 企业链表
听黑马程序员教程<基础数据结构和算法 (C版本)>,照着老师所讲抄的, 视频地址https://www.bilibili.com/video/BV1vE411f7Jh?p=1 喜欢的朋友可 ...
- C语言 - 基础数据结构和算法 - 单向链表
听黑马程序员教程<基础数据结构和算法 (C版本)>,照着老师所讲抄的, 视频地址https://www.bilibili.com/video/BV1vE411f7Jh?p=1 喜欢的朋友可 ...
- C语言- 基础数据结构和算法 - 动态数组
听黑马程序员教程<基础数据结构和算法 (C版本)>,照着老师所讲抄的, 视频地址https://www.bilibili.com/video/BV1vE411f7Jh?p=1 喜欢的朋友可 ...
- 大数据技术之_16_Scala学习_13_Scala语言的数据结构和算法_Scala学习之旅收官之作
第十九章 Scala语言的数据结构和算法19.1 数据结构(算法)的介绍19.2 看几个实际编程中遇到的问题19.2.1 一个五子棋程序19.2.2 约瑟夫问题(丢手帕问题)19.2.3 其它常见算法 ...
随机推荐
- 解决“WARNINGThe remote SSH server rejected X11 forwarding request.“警告
使用xshell连接服务器时,出现了"WARNING! The remote SSH server rejected X11 forwarding request.",意思是&qu ...
- Mybatis-自定义类型处理器
类型转换器:mybatis中有一些常用的类型转换器,比如把Java中的short类型转换为mysql中的short类型:但是如果现在是Java中的Date类型,但是我想要存储到数据库中转换为Long类 ...
- YOLO系列梳理(一)YOLOv1-YOLOv3
前言 本文是YOLO系列专栏的第一篇,该专栏将会介绍YOLO系列文章的算法原理.代码解析.模型部署等一系列内容.本文系公众号读者投稿,欢迎想写任何系列文章的读者给我们投稿,共同打造一个计算机视觉技 ...
- Servlet 3.1学习笔记
Servlet 3.1学习笔记 参考文档 Servlet 3.1标准 什么是 Servlet ? Servlet 是基于 Java 平台的 Web 组件,由一个容器管理,能够生成动态内容. 什么是 S ...
- HCIE笔记-第五节-IP地址+VLSM
192.168.1.111 -- 点分十进制 -- IPV4地址表示格式 计算机 只能识别 01010101 二进制 4组十进制数 规则:二进制0/1 在不同位表达的含义是不一致的,0永远代表不取值, ...
- VOC数据集可视化
from gettext import find import os from xml.etree import ElementTree as ET import cv2 def drawBoxOnV ...
- 基于知名微服务框架go-micro开发gRPC应用程序
go-micro是golang的一个微服务框架. go-micro各个版本之间的兼容性问题一直被诟病,前几年go-micro更是分化出了两个分支: 一个延续了go-micro,只不过转到了其公司CEO ...
- LintCode-1173 · 反转字符串 III-题解(istringstream简单使用)
题目链接:https ://www.lintcode.com/problem/1173/?_from=collection&fromId=208描述:给定一个字符串句子,反转句子中每一个单词的 ...
- 变量命名 函数命名 方法 Naming cheatsheet
Naming things is hard. This sheet attempts to make it easier. Although these suggestions can be appl ...
- 五四青年节,今天要学习。汇总5道难度不高但可能遇到的JS手写编程题
壹 ❀ 引 时间一晃,今天已是五一假期最后一天了,没有出门,没有太多惊喜与意外.今天五四青年节,脑子里突然想起鲁迅先生以及悲欢并不相通的话,我的五一经历了什么呢,忍不住想说那大概是,父母教育孩子大声嚷 ...