• 线性表-单链表学习笔记(基础)


    one-to-one

    经典单链表(动态链表)具体实现方案:给每一个元素配置一个指针,每个指针都指向相邻的下一个元素。(“链”字的由来)

    单链表可以由什么组成?
    一个结点(节点)的构成:数据域指针域(Next 域)
    头指针:类型跟指针一样,但是它的特点是永远指向链表中的第一个结点
    结点们头结点:有时为了方便操作,特意在链表开头留一个空结点(代表它数据域不正常利用)
    首元结点:特指链表开头第一个存有了数据的结点
    其它结点:链表中其它的结点
    1. #include
    2. #include
    3. #define ElemType char
    4. //typedef char ElemType
    5. /*结点结构体*/
    6. typedef struct Node{
    7. ElemType data; //数据域
    8. struct Node* next; //指针域
    9. }Node, *PNode;
    10. /*单链表结构体*/
    11. typedef struct LinkList {
    12. PNode first; //指向头结点
    13. PNode last; //指向尾结点
    14. size_t size; //记录有效结点数
    15. }LinkList;
    16. /*初始化*/
    17. void init(LinkList* ll)
    18. {
    19. ll->first = ll->last = (Node*)malloc(sizeof(Node)); //为ll的头、尾结点申请内存空间
    20. if (ll->first == NULL)
    21. return;
    22. ll->first->next = NULL; //初始化头结点的指针域
    23. ll->size = 0; //初始化ll的结点数
    24. }
    25. /*判空*/
    26. int empty(LinkList* ll)
    27. {
    28. if (ll->size == 0)
    29. return 1;
    30. else
    31. return 0;
    32. }
    33. /*长度*/
    34. size_t size(LinkList* ll)
    35. {
    36. return ll->size;
    37. }
    38. /*尾插*/
    39. void push_back(LinkList* ll, ElemType value)
    40. {
    41. PNode p = (Node*)malloc(sizeof(Node)); //申请内存空间
    42. if (p == NULL)
    43. return;
    44. p->data = value; //设置值
    45. p->next = NULL; //初始化next域
    46. ll->last->next = p; //将结点放到链表尾部
    47. ll->last = p; //包裹指针下移
    48. ll->size++; //增加结点数
    49. }
    50. /*头插*/
    51. void push_front(LinkList* ll, ElemType value)
    52. {
    53. PNode p = (Node*)malloc(sizeof(Node)); //申请内存空间
    54. if (p == NULL)
    55. return;
    56. p->data = value; //设置值
    57. p->next = ll->first->next;
    58. ll->first->next = p;
    59. if (ll->size == 0)
    60. {
    61. ll->last = p;
    62. }
    63. ll->size++; //增加结点数
    64. }
    65. /*尾删*/
    66. void pop_back(LinkList* ll)
    67. {
    68. if (ll->size == 0) //判断链表是否还有结点?
    69. return; //直接退出
    70. PNode p = ll->first; //缓冲变量p
    71. while (p->next != ll->last) //先找到倒数第二个结点,再代替
    72. p = p->next; //下移指针
    73. free(ll->last); //释放内存
    74. ll->last = p; //取代
    75. ll->last->next = NULL; //初始化尾结点的指针域
    76. ll->size--; //减少结点数
    77. }
    78. /*头删*/
    79. void pop_front(LinkList* ll)
    80. {
    81. if (ll->size == 0)
    82. return;
    83. PNode p = ll->first->next; //临时保存首元结点的地址
    84. ll->first->next = p->next; //解除连接
    85. free(p); //释放内存
    86. if (ll->size == 1)
    87. ll->last = ll->first;
    88. ll->size--;
    89. }
    90. /*值查找*/
    91. Node* find(LinkList* ll, ElemType key)
    92. {
    93. PNode p = ll->first->next;
    94. while (p!=NULL && p->data!=key)
    95. p = p->next;
    96. return p;
    97. }
    98. /*输出*/
    99. void display(LinkList* ll)
    100. {
    101. PNode p = ll->first->next;
    102. while (p != NULL)
    103. {
    104. printf("%c -> ", p->data);
    105. p = p->next;
    106. }
    107. printf("Nul.\n");
    108. }
    109. /*反转:beg、mid、end*/
    110. LinkList* iteration_reverse(LinkList* ll)
    111. {
    112. if (ll->first == NULL || ll->first->next == NULL)
    113. {
    114. return ll;
    115. }
    116. else
    117. {
    118. Node* beg = NULL;
    119. Node* mid = ll->first->next;
    120. Node* temp = ll->first->next;
    121. Node* end = temp->next;
    122. while (1)
    123. {
    124. mid->next = beg; //指针域换向
    125. if (end == NULL)
    126. {
    127. //push_back(&ll, NULL);
    128. break;
    129. }
    130. beg = mid;
    131. mid = end;
    132. end = end->next;
    133. }
    134. ll->first->next = mid;
    135. return ll;
    136. }
    137. }
    138. int main() {
    139. LinkList ll = { NULL, NULL, 0 };
    140. init(&ll);
    141. printf("此单链表是否为空?(1代表true,0代表false)\n%d\n", empty(&ll));
    142. push_back(&ll, 'h');
    143. push_back(&ll, 'e');
    144. push_back(&ll, 'l');
    145. push_back(&ll, 'l');
    146. push_back(&ll, 'o');
    147. printf("插入hello的单链表的长度:%d\n", size(&ll));
    148. display(&ll);
    149. LinkList* list = iteration_reverse(&ll);
    150. display(list);
    151. printf("插入hello的单链表的长度:%d\n", size(list));
    152. //free(ll.first); //释放
    153. //free(ll.last); //释放
    154. }

    链表和顺序表的区分

    链表顺序表
    内存申请时机需要提前申请什么时候存储,什么时候申请
    复用性一次开辟,永久使用(除了动态数组)随用随开
    空间利用率链表 < 顺序表(链表明显容易产生空间碎片)
    时间复杂度链表适合操作元素,顺序表适合访问查找元素

    单链表结构2:

    1. #include
    2. #include
    3. //链表中节点的结构
    4. typedef struct link {
    5. int elem;
    6. struct link* next;
    7. }Link;
    8. /*初始化*/
    9. Link* initLink()
    10. {
    11. int i; //缓冲变量i
    12. //1、创建头指针
    13. Link* p = NULL;
    14. //2、创建头结点
    15. Link* temp = (Link*)malloc(sizeof(Link));
    16. temp->elem = 0;
    17. temp->next = NULL;
    18. //头指针指向头结点
    19. p = temp;
    20. //3、每创建一个结点,都令其直接前驱结点的指针指向它
    21. for (i = 1; i < 5; i++) {
    22. //创建一个结点
    23. Link* a = (Link*)malloc(sizeof(Link));
    24. a->elem = i;
    25. a->next = NULL;
    26. //每次 temp 指向的结点就是 a 的直接前驱结点
    27. temp->next = a;
    28. //temp指向下一个结点(也就是a),为下次添加结点做准备
    29. temp = temp->next;
    30. }
    31. return p;
    32. }
    33. /*插入*/
    34. void insertElem(Link* p, int elem, int add)
    35. {
    36. int i;
    37. Link* c = NULL;
    38. Link* temp = p;//创建临时结点temp
    39. //首先找到要插入位置的上一个结点
    40. for (i = 1; i < add; i++) {
    41. temp = temp->next;
    42. if (temp == NULL) {
    43. printf("插入位置无效\n");
    44. return;
    45. }
    46. }
    47. //创建插入结点c
    48. c = (Link*)malloc(sizeof(Link));
    49. c->elem = elem;
    50. //① 将新结点的 next 指针指向插入位置后的结点
    51. c->next = temp->next;
    52. //② 将插入位置前结点的 next 指针指向插入结点;
    53. temp->next = c;
    54. }
    55. /*删除*/
    56. int delElem(Link** p, int elem) //p为原链表,elem 为要删除的目标元素
    57. {
    58. Link* del = NULL, *temp = *p;
    59. //删除首元结点需要单独考虑
    60. if (temp->elem == elem) {
    61. (*p) = (*p)->next;
    62. free(temp);
    63. return 1;
    64. }
    65. else
    66. {
    67. int find = 0;
    68. //1、找到目标元素的直接前驱结点
    69. while (temp->next) {
    70. if (temp->next->elem == elem) {
    71. find = 1;
    72. break;
    73. }
    74. temp = temp->next;
    75. }
    76. if (find == 0) {
    77. return -1;//删除失败
    78. }
    79. else
    80. {
    81. //标记要删除的结点
    82. del = temp->next;
    83. //2、将目标结点从链表上摘除
    84. temp->next = temp->next->next;
    85. //3、释放目标结点
    86. free(del);
    87. return 1;
    88. }
    89. }
    90. }
    91. /*更新*/
    92. int amendElem(Link* p, int oldElem, int newElem) //p 为有头结点的链表,oldElem 为旧元素,newElem 为新元素
    93. {
    94. p = p->next;
    95. while (p) {
    96. if (p->elem == oldElem) {
    97. p->elem = newElem;
    98. return 1;
    99. }
    100. p = p->next;
    101. }
    102. return -1;
    103. }
    104. //p为原链表,elem表示被查找元素
    105. int selectElem(Link* p, int elem) {
    106. int i = 1;
    107. //带头结点,p 指向首元结点
    108. p = p->next;
    109. while (p) {
    110. if (p->elem == elem) {
    111. return i;
    112. }
    113. p = p->next;
    114. i++;
    115. }
    116. return -1;//返回-1,表示未找到
    117. }
    118. //输出链表中各个结点的元素
    119. void display(Link* p) {
    120. p = p->next;
    121. while (p) {
    122. printf("%d ", p->elem);
    123. p = p->next;
    124. }
    125. printf("\n");
    126. }
    127. //释放链表
    128. void Link_free(Link* p) {
    129. Link* fr = NULL;
    130. while (p->next)
    131. {
    132. fr = p->next;
    133. p->next = p->next->next;
    134. free(fr);
    135. }
    136. free(p);
    137. }
    138. int main() {
    139. Link* p = initLink();
    140. printf("初始化链表为:\n");
    141. display(p);
    142. printf("在第 3 的位置上添加元素 6:\n");
    143. insertElem(p, 6, 3);
    144. display(p);
    145. printf("删除元素4:\n");
    146. delElem(p, 4);
    147. display(p);
    148. printf("查找元素 2:\n");
    149. printf("元素 2 的位置为:%d\n", selectElem(p, 2));
    150. printf("更改元素 1 的值为 6:\n");
    151. amendElem(p, 1, 6);
    152. display(p);
    153. Link_free(p);
    154. return 0;
    155. }

    单链表的反转

    即头尾颠倒,方向翻转

  • 相关阅读:
    GRS全球回收标准-未来趋势
    Object类,以及__new__,__init__,__setattr__,__dict__
    史上最强,Jmeter性能测试-性能场景设计实例(详全)
    基于shiro+redis缓存的session共享方案
    苹果认证Apple Store Connenct api的使用
    智能井盖传感器:数智赋能让城市管理更智慧
    nodejs中使用ffmpeg零基础教程(electron+vue3)
    Idea 2023.2.5配置(插件、Maven等)
    enter ubuntu boot option in virt-manager
    杰理之、产线装配环节【篇】
  • 原文地址:https://blog.csdn.net/2301_76632538/article/details/134257007