• 嵌入式学习——数据结构(双向无头有环链表、内核链表、栈)——day48


    1. 约瑟夫环问题——双向无头回环链表

    1.1 问题描述

            给定 ( n ) 个人(编号为 ( 1, 2, \ldots, n )),他们围成一个圈。从第一个人开始报数,每报到第 ( k ) 个人时,杀掉这个人,然后从下一个人重新开始报数。重复这个过程,直到所有人都被杀死。约瑟夫环问题是要确定最后一个幸存者的编号。

    1.2 实质

            每次删除循环链表中的一个节点,直到链表中仅剩一个节点结束

    2. 双向无头循环链表代码

    2.1 makefile

    1. OBJ:=a.out
    2. OBJS+=main.c doublelink.c
    3. CCl=gcc
    4. $(OBJ):$(OBJS)
    5. $(CC) $^ -o $@
    6. .PHONY:
    7. clean:
    8. rm $(OBJ)
    9. test:
    10. valgrind --tool=memcheck --leak-check=full ./$(OBJ)

    2.2 double.h

    1. #ifndef _DOUBLELINK_H_
    2. #define _DOUBLELINK_H_
    3. typedef struct stu
    4. {
    5. int id;
    6. char name[32];
    7. int score;
    8. }DataType;
    9. typedef struct node
    10. {
    11. DataType data;
    12. struct node *ppre;
    13. struct node *pnext;
    14. }DouNode;
    15. typedef struct list
    16. {
    17. DouNode *phead;
    18. int clen;
    19. }DouList;
    20. extern DouList *create_dou_link();
    21. extern int is_empty_dou_link(DouList *plist);
    22. extern void dou_link_for_each(DouList *plist, int dir);
    23. extern int push_head_dou_link(DouList *plist, DataType data);
    24. extern int push_tail_dou_link(DouList *plist, DataType data);
    25. extern int pop_head_dou_link(DouList *plist);
    26. extern int pop_tail_dou_link(DouList *plist);
    27. extern void loop_dou_link(DouList *plist);
    28. extern DouNode *Joseph_loop(DouList *plist);
    29. extern void dou_link_for_remain(DouList *plist);
    30. #endif

    2.3 double.c

    1. #include "doublelink.h"
    2. #include
    3. #include
    4. #include
    5. DouList *create_dou_link()//创建标签
    6. {
    7. DouList *plist = NULL;
    8. plist = (DouList *)malloc(sizeof(DouList));
    9. if (NULL == plist)
    10. {
    11. perror("fail to malloc");
    12. return NULL;
    13. }
    14. plist->phead = NULL;
    15. plist->clen = 0;
    16. return plist;
    17. }
    18. int is_empty_dou_link(DouList *plist)//判断空链表
    19. {
    20. if (NULL == plist->phead)
    21. {
    22. return 1;
    23. }
    24. return 0;
    25. }
    26. int push_head_dou_link(DouList *plist, DataType data)//头插
    27. {
    28. DouNode *pnode = NULL;
    29. pnode = malloc(sizeof(DouNode));
    30. if (NULL == pnode)
    31. {
    32. perror("fail to malloc");
    33. return -1;
    34. }
    35. pnode->data = data;
    36. pnode->ppre = NULL;
    37. pnode->pnext = NULL;
    38. if (is_empty_dou_link(plist))//空链表直接插
    39. {
    40. plist->phead = pnode;
    41. }
    42. else
    43. {
    44. pnode->pnext = plist->phead;
    45. plist->phead->ppre = pnode;
    46. plist->phead = pnode;
    47. }
    48. plist->clen++;
    49. return 0;
    50. }
    51. int push_tail_dou_link(DouList *plist, DataType data)//头插
    52. {
    53. DouNode *p = NULL;
    54. DouNode *pnode = NULL;
    55. pnode = malloc(sizeof(DouNode));
    56. if (NULL == pnode)
    57. {
    58. perror("fail to malloc");
    59. return -1;
    60. }
    61. pnode->data = data;
    62. pnode->ppre = NULL;
    63. pnode->pnext = NULL;
    64. if (is_empty_dou_link(plist))//空链表直接插
    65. {
    66. plist->phead = pnode;
    67. }
    68. else
    69. {
    70. p = plist->phead;
    71. while (p->pnext != NULL)
    72. {
    73. p = p->pnext;
    74. }
    75. p->pnext = pnode;
    76. pnode->ppre = p;
    77. }
    78. plist->clen++;
    79. return 0;
    80. }
    81. int pop_head_dou_link(DouList *plist)//头删
    82. {
    83. if (is_empty_dou_link(plist))//空链表直接结束程序
    84. {
    85. return -1;
    86. }
    87. DouNode *pfree = NULL;
    88. pfree = plist->phead;
    89. plist->phead = pfree->pnext;//标签指向第二个节点首地址
    90. if (plist->phead != NULL)//判断是否空链表
    91. {
    92. plist->phead->ppre = NULL;//将第二个节点的ppre变为NULL
    93. }
    94. free(pfree);
    95. plist->clen--;
    96. return 0;
    97. }
    98. int pop_tail_dou_link(DouList *plist)//尾删
    99. {
    100. if (is_empty_dou_link(plist))//空链表程序结束
    101. {
    102. return -1;
    103. }
    104. DouNode *pfree = NULL;
    105. pfree = plist->phead;
    106. while (pfree->pnext)//指针指向最后一个节点
    107. {
    108. pfree = pfree->pnext;
    109. }
    110. if (pfree->ppre != NULL)//链表有两个以上节点
    111. {
    112. pfree->ppre->pnext = NULL;
    113. }
    114. else //链表只有一个节点
    115. {
    116. plist->phead = NULL;
    117. }
    118. free(pfree);
    119. plist->clen--;
    120. return 0;
    121. }
    122. void loop_dou_link(DouList *plist)//将非回环链表改为双向回环链表
    123. {
    124. DouNode *ptmpnode = NULL;
    125. ptmpnode = plist->phead;
    126. while (ptmpnode->pnext != NULL)//将指针移动到末尾节点
    127. {
    128. ptmpnode = ptmpnode->pnext;
    129. }
    130. ptmpnode->pnext = plist->phead;
    131. plist->phead->ppre = ptmpnode;
    132. }
    133. void dou_link_for_remain(DouList *plist)//打印约瑟夫回环一次处理后链表中剩下的成员信息
    134. {
    135. int i = 0;
    136. DouNode *ptmpnode = plist->phead;
    137. for (i = 0; i < plist->clen; i++)
    138. {
    139. printf("%d ", ptmpnode->data.id);
    140. printf("%s ", ptmpnode->data.name);
    141. printf("%d\n", ptmpnode->data.score);
    142. ptmpnode = ptmpnode->pnext;
    143. }
    144. printf("=========================\n");
    145. }
    146. DouNode *Joseph_loop(DouList *plist)//约瑟夫回环、实质是删除链表节点直到留下最后一个节点为止
    147. {
    148. DouNode *pfreenode = NULL;//
    149. DouNode *ptmpnode = NULL;//指向回环
    150. ptmpnode = plist->phead;
    151. while (ptmpnode != ptmpnode->pnext)//判断回环是否只剩下一个节点
    152. {
    153. pfreenode = ptmpnode;//指向当前所在回环的位置
    154. pfreenode = pfreenode->pnext->pnext;//回环向后移动两个单位
    155. pfreenode->ppre->pnext = pfreenode->pnext;
    156. pfreenode->pnext->ppre = pfreenode->ppre;
    157. ptmpnode = pfreenode->pnext;//记录要删除的回环的下一个位置,保证循环的延续
    158. if (pfreenode == plist->phead)//判断要删除的节点是否是表头后的第一个节点、若是,给表头接入要删除节点的下一个节点
    159. {
    160. plist->phead = ptmpnode;
    161. }
    162. free(pfreenode);
    163. plist->clen--;
    164. dou_link_for_remain(plist);//打印链表中剩下的节点信息
    165. }
    166. return ptmpnode;
    167. }

    2.4 main.c

    1. #include
    2. #include
    3. #include "doublelink.h"
    4. int main(void)
    5. {
    6. DataType stus[] = {{1, "doinb", 100},
    7. {2, "lwx", 67},
    8. {3, "lqs", 99},
    9. {4, "tian", 98},
    10. {5, "gimgoon", 78},
    11. {6, "xinyi", 88},
    12. {7, "nuguri", 99},
    13. {8, "khan", 77},
    14. {9, "bo", 94},
    15. {10, "xiaolaohu", 60}
    16. };
    17. DouNode *ptmpnode = NULL;
    18. int i = 0;
    19. DouList *plist = create_dou_link();//表头创建
    20. if (NULL == plist)
    21. {
    22. return -1;
    23. }
    24. for (i = 0; i < sizeof(stus) / sizeof(stus[0]); i++)//给链表中插入结构体中的所有内容
    25. {
    26. push_tail_dou_link(plist, stus[i]);//尾插
    27. }
    28. dou_link_for_each(plist, 1);
    29. dou_link_for_each(plist, 0);
    30. loop_dou_link(plist);//创建双向回环链表
    31. ptmpnode = Joseph_loop(plist);//约瑟夫回环
    32. printf("%s\n", ptmpnode->data.name);
    33. return 0;
    34. }

    2.5  判断单向链表是否有环

            利用快慢指针,慢指针走一步,快指针走两步

            快指针每走一步,判断是否为空或者是否与慢指针相遇,相遇为有环链表

    3. 内核链表(有头、双向循环链表)

    3.1 定义

            Linux内核链表是一种双向循环链表,它的实现非常简洁而高效,主要通过一些宏和内联函数来操作链表。链表节点的结构定义在头文件 中。

    3.1 offsetof宏

            获取结构体某个成员到结构体开头的偏移量

    3.2 container_of宏

            通过offsetof偏移量获取结构体的首地址

    3. 栈

    3.1 定义

            栈(Stack)是一种抽象的数据结构,它遵循后进先出(LIFO, Last In First Out)的原则。也就是说,最后放入栈中的元素最先被取出。

    3.2 栈的基本操作

            1. 入栈、压栈:将一个元素放入栈顶。

            2. 出栈、弹栈:从栈顶移除一个元素。

            3. 取栈顶元素:查看栈顶元素但不移除它。

            4. 判断栈是否为空:检查栈中是否有元素。

    3.3 分类

            (1)按实现方式分类:栈分为顺序栈和链式栈

            1. 顺序栈

                   使用数组实现的栈,数组中的元素按顺序存储。优点是实现简单,访问效率高;缺点是栈的容量固定,扩展不便。 

            2. 链式栈

            使用链表实现的栈,链表的每个节点存储一个栈元素。优点是栈的容量可以动态扩展;缺点是指针操作复杂,访问效率相对较低。

            (2)按用途来分类

            1. 操作系统栈

            用于管理程序执行时的函数调用,保存函数调用的返回地址、本地变量等信息。操作系统栈通常是顺序栈,采用固定大小。

                    1. 局部变量

                    2. 函数的形参、返回值

                    3. 函数调用关系——保护现场、恢复现场

    3.4 数据结构中的栈——链式栈

    4. 面试考点

            区分满增栈、满减栈、空增栈、空减栈(前提:仅限于顺序栈,数组方式构成的)

    4.1 满栈和空栈——判断栈顶所在位置是否存有数据而非整个栈有没有数据

            1. 满栈:栈顶所在位置有数据

                    入栈操作流程:先向上移动栈顶指针,再将数据压入栈中

            2. 空栈:栈顶所在位置没有数据

                    入栈操作流程:先将数据压入栈顶,再向上移动栈顶指针


     

    4.2 增栈和减栈——判断栈的生长方向

            0x1000与0x2000,内存高地址为0x2000,内存低地址为0x1000

            1. 增栈:数据入栈时栈顶指针向内存高地址移动

            2. 减栈:数据入栈时栈顶指针向内存低地址移动

    (1) 满增栈

            1. 出栈时:栈顶指针向内存高地址移动,再向栈顶入栈数据,

            2. 出栈时:出栈数据,栈顶指针向内存低地址移动,

    (2) 满减栈

    (3) 空增栈

            1. 出栈时:先向栈顶入栈数据,栈顶指针向内存高地址移动

            2. 出栈时:栈顶指针向内存低地址移动,出栈数据

    (4) 空减栈

    5. 数据结构中的栈——链式栈

    5.1 代码

    (1)makefile

    1. OBJ:=a.out
    2. OBJS+=main.c stack.c
    3. CCl=gcc
    4. $(OBJ):$(OBJS)
    5. $(CC) $^ -o $@
    6. .PHONY:
    7. clean:
    8. rm $(OBJ)
    9. test:
    10. valgrind --tool=memcheck --leak-check=full ./$(OBJ)

    注意:在终端输入make test可以测试销毁是否成功以及是否有内存泄漏

    (2)stack.h

    1. #ifndef _STACK_H_
    2. #define _STACK_H_
    3. typedef int DataType;
    4. typedef struct stact_node
    5. {
    6. DataType data;
    7. struct stact_node *pnext;
    8. }StackNode;
    9. typedef struct Stack
    10. {
    11. StackNode *ptop;
    12. int clen;
    13. }StackList;
    14. extern StackList *create_stack();
    15. extern int is_empty_stack(StackList *plist);
    16. extern int push_stack(StackList *plist, DataType data);//入栈头插
    17. extern void stack_for_each(StackList *plist);
    18. extern int pop_stack(StackList *plist, DataType *pdata);//出栈头删
    19. extern void clear_stack(StackList *plist);
    20. extern void destory_stack(StackList *plist);
    21. extern int get_stack_top(StackList *plist, DataType *pdata);
    22. #endif

    (3)stack.c

    1. #include
    2. #include
    3. #include "stack.h"
    4. StackList *create_stack()
    5. {
    6. StackList *plist = malloc(sizeof(StackList));
    7. if (NULL == plist)
    8. {
    9. perror("fail to malloc");
    10. return NULL;
    11. }
    12. plist->ptop = NULL;
    13. plist->clen = 0;
    14. return plist;
    15. }
    16. int is_empty_stack(StackList *plist)
    17. {
    18. if (NULL == plist->ptop)
    19. {
    20. return 1;
    21. }
    22. return 0;
    23. }
    24. int push_stack(StackList *plist, DataType data)//入栈头插
    25. {
    26. StackNode *pnode = malloc(sizeof(StackNode));
    27. if (NULL == pnode)
    28. {
    29. perror("fail to malloc");
    30. return -1;
    31. }
    32. pnode->data = data;
    33. pnode->pnext = NULL;
    34. pnode->pnext = plist->ptop;
    35. plist->ptop = pnode;
    36. plist->clen++;
    37. return 0;
    38. }
    39. void stack_for_each(StackList *plist)
    40. {
    41. StackNode *ptmp = plist->ptop;
    42. while (ptmp != NULL)
    43. {
    44. printf("%d ", ptmp->data);
    45. ptmp = ptmp->pnext;
    46. }
    47. printf("\n");
    48. }
    49. int pop_stack(StackList *plist, DataType *pdata)//出栈头删
    50. {
    51. if (is_empty_stack(plist))
    52. {
    53. return -1;
    54. }
    55. StackNode *pfree = plist->ptop;
    56. plist->ptop = pfree->pnext;
    57. if (pdata != NULL)//传入非空地址,将删除的节点的数据传出
    58. {
    59. *pdata = pfree->data;
    60. }
    61. free(pfree);
    62. plist->clen--;
    63. return 0;
    64. }
    65. void clear_stack(StackList *plist)//清空栈区
    66. {
    67. while (!is_empty_stack(plist))
    68. {
    69. pop_stack(plist, NULL);
    70. }
    71. }
    72. void destory_stack(StackList *plist)
    73. {
    74. if (!is_empty_stack(plist))
    75. {
    76. clear_stack(plist);
    77. }
    78. free(plist);
    79. }
    80. int get_stack_top(StackList *plist, DataType *pdata)//获得栈顶数据
    81. {
    82. if (is_empty_stack(plist))
    83. {
    84. return -1;
    85. }
    86. *pdata = plist->ptop->data;
    87. return 0;
    88. }

    (4)main.c

    1. #include
    2. #include
    3. #include "stack.h"
    4. int main(void)
    5. {
    6. DataType tmpdata = 0;
    7. DataType data[] = {1, 2, 3, 4, 5};
    8. StackNode *ptmpnode = NULL;
    9. int i = 0;
    10. int ret = 0;
    11. StackList *plist = create_stack();//创建栈表头
    12. if (NULL == plist)
    13. {
    14. return -1;
    15. }
    16. for (i = 0; i < sizeof(data) / sizeof(data[0]); i++)//入栈所有数据
    17. {
    18. push_stack(plist, data[i]);//入栈头插
    19. }
    20. stack_for_each(plist);//遍历打印所有数据
    21. #if 0
    22. for (i = 0; i < sizeof(data)/sizeof(data[0]); i++)//获取栈顶元素并打印,出栈数据,打印栈中的剩下元素
    23. {
    24. printf("======== %d ========", i);
    25. ret = get_stack_top(plist, &tmpdata);
    26. if (0 == ret)
    27. {
    28. printf("ptop data:%d ", tmpdata);
    29. }
    30. pop_stack(plist, NULL);
    31. stack_for_each(plist);//遍历打印所有数据
    32. }
    33. #endif
    34. #if 0
    35. clear_stack(plist);//清理栈中所有节点
    36. if (is_empty_stack(plist))
    37. {
    38. printf("clear_stack success!\n");
    39. }
    40. #endif
    41. #if 1
    42. destory_stack(plist);
    43. #endif
    44. return 0;
    45. }

    5.2  应用

                    1. 撤回操作

                    2. 浏览器返回上一层操作

                    3. 计算器

            示例:利用链式栈实现四则运算

            1. makefile

    1. OBJ:=a.out
    2. OBJS+=calculate.c stack.c
    3. CCl=gcc
    4. $(OBJ):$(OBJS)
    5. $(CC) $^ -o $@
    6. .PHONY:
    7. clean:
    8. rm $(OBJ)
    9. test:
    10. valgrind --tool=memcheck --leak-check=full ./$(OBJ)

            2. calculate.c

    1. #include
    2. #include
    3. #include
    4. #include "stack.h"
    5. int is_char_num(char ch)//判断此指针是否指向数字
    6. {
    7. if (ch >= '0' && ch <= '9')
    8. {
    9. return 1;
    10. }
    11. return 0;
    12. }
    13. int get_opt_level(char opt)//获得运算符优先级
    14. {
    15. switch(opt)
    16. {
    17. case '+':
    18. case '-':
    19. return 1;
    20. case '*':
    21. case '/':
    22. return 2;
    23. }
    24. }
    25. int get_result(int num1, int num2, char opt)//获得运算结果
    26. {
    27. switch(opt)
    28. {
    29. case '+':return num1 + num2;
    30. case '-':return num1 - num2;
    31. case '/':return num1 / num2;
    32. case '*':return num1 * num2;
    33. }
    34. }
    35. int main(int argc, const char *argv[])
    36. {
    37. char press[128] = {0};//运算表达式
    38. DataType num = 0;//存储每个运算数
    39. DataType data;
    40. DataType opt;
    41. DataType num1;
    42. DataType num2;
    43. DataType res = 0; //计算器返回值
    44. StackList *pNumStack = create_stack();//存储数字的栈
    45. StackList *pOptStack = create_stack();//存储运算符的栈
    46. fgets(press, sizeof(press), stdin);
    47. press[strlen(press)-1] = '\0';
    48. char *p = press;//遍历运算式字符串
    49. while (1)
    50. {
    51. if ('\0' == *p && is_empty_stack(pOptStack))//循环结束的条件,指针遍历到字符串末尾,运算符栈为空
    52. {
    53. break;
    54. }
    55. /*判断条件1*/
    56. while (is_char_num(*p))//拆解数字
    57. {
    58. num = num * 10 + (*p - '0');
    59. p++;
    60. if (!is_char_num(*p))
    61. {
    62. push_stack(pNumStack, num);//入栈数字
    63. num = 0;
    64. }
    65. }
    66. /*判断条件2*/
    67. if (is_empty_stack(pOptStack))//运算符栈为空,入栈
    68. {
    69. push_stack(pOptStack, *p);
    70. p++;
    71. continue;
    72. }
    73. /*判断条件3*/
    74. if ('\0' == *p && !is_empty_stack(pOptStack))//结束条件
    75. {
    76. pop_stack(pOptStack, &opt);
    77. pop_stack(pNumStack, &num2);
    78. pop_stack(pNumStack, &num1);
    79. res = get_result(num1, num2, opt);
    80. push_stack(pNumStack, res);
    81. continue;
    82. }
    83. /*判断条件4*/
    84. get_stack_top(pOptStack, &data);
    85. if (get_opt_level(data) < get_opt_level(*p))//运算符栈不为空,本次运算符优先级大于栈顶中运算符优先级
    86. {
    87. push_stack(pOptStack, *p);
    88. p++;
    89. }
    90. else if (get_opt_level(data) >= get_opt_level(*p))
    91. {
    92. pop_stack(pOptStack, &opt);
    93. pop_stack(pNumStack, &num2);
    94. pop_stack(pNumStack, &num1);
    95. res = get_result(num1, num2, opt);
    96. push_stack(pNumStack, res);
    97. }
    98. }
    99. get_stack_top(pNumStack, &res);
    100. printf("res = %d\n", res);
    101. destaroy_stack(pNumStack);
    102. destaroy_stack(pOptStack);
    103. return 0;
    104. }

            3. stack.c

    1. #include "stack.h"
    2. #include
    3. #include
    4. StackList *create_stack()
    5. {
    6. StackList *pstack = malloc(sizeof(StackList));
    7. if (NULL == pstack)
    8. {
    9. perror("fail malloc");
    10. return NULL;
    11. }
    12. pstack->ptop = NULL;
    13. pstack->clen = 0;
    14. return pstack;
    15. }
    16. int is_empty_stack(StackList *pstack)
    17. {
    18. if (NULL == pstack->ptop)
    19. {
    20. return 1;
    21. }
    22. return 0;
    23. }
    24. int push_stack(StackList *pstack, DataType data)
    25. {
    26. StackNode *pnode = malloc(sizeof(StackNode));
    27. if (NULL == pnode)
    28. {
    29. perror("fail malloc");
    30. return -1;
    31. }
    32. pnode->data = data;
    33. pnode->pnext = NULL;
    34. pnode->pnext = pstack->ptop;
    35. pstack->ptop = pnode;
    36. pstack->clen++;
    37. return 0;
    38. }
    39. int pop_stack(StackList *pstack, DataType *pdata)
    40. {
    41. if (is_empty_stack(pstack))
    42. {
    43. return 1;
    44. }
    45. StackNode *pfree = pstack->ptop;
    46. pstack->ptop = pfree->pnext;
    47. if (pdata != NULL)
    48. {
    49. *pdata = pfree->data;
    50. }
    51. free(pfree);
    52. pstack->clen--;
    53. return 0;
    54. }
    55. void clear_stack(StackList * pstack)
    56. {
    57. while (!is_empty_stack(pstack))
    58. {
    59. pop_stack(pstack, NULL);
    60. }
    61. }
    62. void destaroy_stack(StackList *pstack)
    63. {
    64. clear_stack(pstack);
    65. free(pstack);
    66. }
    67. int get_stack_top(StackList *pstack, DataType *pdata)
    68. {
    69. if (is_empty_stack(pstack))
    70. {
    71. return 1;
    72. }
    73. *pdata = pstack->ptop->data;
    74. return 0;
    75. }

            4. stack.h

    1. #ifndef __STACK_H__
    2. #define __STACK_H__
    3. typedef int DataType;
    4. typedef struct stk_node
    5. {
    6. DataType data;
    7. struct stk_node *pnext;
    8. }StackNode;
    9. typedef struct Stack
    10. {
    11. StackNode *ptop;
    12. int clen;
    13. }StackList;
    14. StackList *create_stack();
    15. int is_empty_stack(StackList *);
    16. int push_stack(StackList *, DataType );
    17. int pop_stack(StackList *, DataType *);
    18. void clear_stack(StackList *);
    19. void destaroy_stack(StackList *);
    20. int get_stack_top(StackList *, DataType *);
    21. #endif

            

            

  • 相关阅读:
    云原生Docker网络管理
    SpringCach
    零基础学算法100天第8天——双指针(基础算法)
    AJAX之数据交换
    centos7安装git客户端
    【buildroot】buildroot使用笔记-03 | 系统初始化的三种方式
    linux用户及用户组的分类、管理
    手把手教会你|Sockets多用户-服务器数据库编程
    18. 机器学习——集成学习
    腾讯云轻量应用服务器性能如何?来自学生的评价
  • 原文地址:https://blog.csdn.net/qq_47798402/article/details/139793109