C语言- 基础数据结构和算法 - 循环链表

2022/6/6 1:19:44

本文主要是介绍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语言- 基础数据结构和算法 - 循环链表的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程