• 线性表——单链表的增删查改


      本节复习链表的增删查改

    首先, 链表不是连续的, 而是通过指针联系起来的。 如图:

    这四个节点不是连续的内存空间, 但是彼此之间使用了一个指针来连接。 这就是链表。 

    现在我们来实现链表的增删查改。

    目录

    单链表的全部接口:

     准备文件

    建立结构体蓝图

    申请链表节点函数接口

    单链表的打印函数接口

    单链表尾插函数接口

    单链表头插函数接口

     单链表尾删函数接口

    单链表的头删函数接口

     单链表查找函数接口

    单链表pos位置之后插入数据接口

    单链表删除pos之后位置的数据

    单链表在pos位置之前插入数据接口

    单链表删除pos位置数据接口

    单链表的销毁


    单链表的全部接口:

    //申请链表节点函数接口
    SLNode* BuySListNode(SLTDataType x);
    //单链表的打印函数接口
    void SListPrint(SLNode* phead);
    //单链表尾插函数接口
    void SListPushBack(SLNode** pphead, SLTDataType x);
    //单链表头插函数接口
    void SListPushFront(SLNode** pphead, SLTDataType x);
    //单链表尾删函数接口
    void SListPopBack(SLNode** pphead);
    //单链表的头删函数接口
    void SListPopFront(SLNode** pphead);
    //单链表查找函数接口
    SLNode* SListFind(SLNode* phead, SLTDataType x);
    //单链表在pos位置之后插入数据接口
    void SListInsertAfter(SLNode* pos, SLTDataType x);
    //单链表在pos之后的位置删除数据
    void SListPopAfter(SLNode* pos);
    //单链表在pos位置之前插入数据接口
    void SListInsert(SLNode** pphead, SLNode* pos, SLTDataType x);
    //单链表在pos位置删除数据接口
    void SListPop(SLNode** pphead, SLNode* pos);
    //单链表的销毁函数接口
    void SLTDestory(SLNode** pphead);
     

    ---------------------------------------------------------------------------------------------------------------------------------

     准备文件

    首先准备好三个文件夹, 一个main.c文件夹, 一个.h文件夹用来声明链表的接口以及定义结构体等。 一个.c文件夹用来实现单链表。

    ---------------------------------------------------------------------------------------------------------------------------------

    建立结构体蓝图

    首先包含一下头文件, 定义一下数据类型。

    1. #include
    2. #include
    3. #include
    4. typedef int SLTDataType;

    接着再建立一个链表的结构体

    1. #include
    2. #include
    3. #include
    4. typedef int SLTDataType;
    5. typedef struct SListNode
    6. {
    7. struct SListNode* next;
    8. SLTDataType data;
    9. }SLNode;

    ---------------------------------------------------------------------------------------------------------------------------------

    申请链表节点函数接口

    申请链表的节点操作, 在尾插, 头插, 或者特定位置插入的时候都需要, 所以可以封装成一个函数。 后续直接进行复用就可以。

    .h函数声明

    1. 链表的增删查改///
    2. #include
    3. #include
    4. #include
    5. typedef int SLTDataType;
    6. typedef struct SListNode //创建结构体
    7. {
    8. struct SListNode* next;
    9. SLTDataType data;
    10. }SLNode;
    11. //链表接口声明/
    12. //申请链表节点函数接口
    13. SLNode* BuySListNode(SLTDataType x);

    .c函数实现

    1. 单链表函数实现函数接口
    2. //单链表申请节点函数接口
    3. SLNode* BuySListNode(SLTDataType x)
    4. {
    5. SLNode* NewNode = (SLNode*)malloc(sizeof(SLNode));
    6. if (NewNode == NULL)
    7. {
    8. printf("申请节点失败\n");
    9. return;
    10. }
    11. //
    12. NewNode->data = x;
    13. NewNode->next = NULL;
    14. }

     在实现的过程中,可以将数据直接储存到新节点中。 然后让新节点指向NULL, 然后返回该节点。 然后将链表直接连接到这个节点就可以。

    ---------------------------------------------------------------------------------------------------------------------------------

    单链表的打印函数接口

    为了便于后续的函数接口的调试, 我们先实现单链表的打印操作。

    .h函数声明

    1. 链表的增删查改///
    2. #include
    3. #include
    4. #include
    5. typedef int SLTDataType;
    6. typedef struct SListNode
    7. {
    8. struct SListNode* next;
    9. SLTDataType data;
    10. }SLNode;
    11. //链表接口声明/
    12. //申请链表节点函数接口
    13. SLNode* BuySListNode(SLTDataType x);
    14. //单链表的打印函数接口
    15. void SListPrint(SLNode* phead);

    .c函数实现

    1. 单链表函数实现函数接口
    2. //单链表申请节点函数接口
    3. SLNode* BuySListNode(SLTDataType x)
    4. {
    5. SLNode* NewNode = (SLNode*)malloc(sizeof(SLNode));
    6. if (NewNode == NULL)
    7. {
    8. printf("申请节点失败\n");
    9. return;
    10. }
    11. //
    12. NewNode->data = x;
    13. NewNode->next = NULL;
    14. }
    15. //单链表的节点打印操作
    16. void SListPrint(SLNode* phead)
    17. {
    18. SLNode* cur = phead;
    19. while (cur != NULL)
    20. {
    21. printf("%d->", cur->data);
    22. cur = cur->next;
    23. }
    24. if (cur == NULL)//最后打印一个NULL
    25. {
    26. printf("NULL");
    27. }
    28. }

    ---------------------------------------------------------------------------------------------------------------------------------

    单链表尾插函数接口

    .h函数声明

    1. 链表的增删查改///
    2. #include
    3. #include
    4. #include
    5. typedef int SLTDataType;
    6. typedef struct SListNode
    7. {
    8. struct SListNode* next;
    9. SLTDataType data;
    10. }SLNode;
    11. //链表接口声明/
    12. //申请链表节点函数接口
    13. SLNode* BuySListNode(SLTDataType x);
    14. //单链表的打印函数接口
    15. void SListPrint(SLNode* phead);
    16. //单链表尾插函数接口
    17. void SListPushBack(SLNode** pphead, SLTDataType x);

    .c函数实现

    1. 单链表函数实现函数接口
    2. //单链表申请节点函数接口
    3. SLNode* BuySListNode(SLTDataType x)
    4. {
    5. SLNode* NewNode = (SLNode*)malloc(sizeof(SLNode));
    6. if (NewNode == NULL)
    7. {
    8. printf("申请节点失败\n");
    9. return;
    10. }
    11. //
    12. NewNode->data = x;
    13. NewNode->next = NULL;
    14. }
    15. //单链表的节点打印操作
    16. void SListPrint(SLNode* phead)
    17. {
    18. SLNode* cur = phead;
    19. while (cur != NULL)
    20. {
    21. printf("%d->", cur->data);
    22. cur = cur->next;
    23. }
    24. if (cur == NULL)//最后打印一个NULL
    25. {
    26. printf("NULL");
    27. }
    28. }
    29. //单链表尾插函数接口
    30. void SListPushBack(SLNode** pphead, SLTDataType x)
    31. {
    32. assert(pphead);
    33. SLNode* newnode = BuySListNode(x);//利用申请节点函数申请节点
    34. SLNode* cur = *pphead; //让cur指向phead所指向空间
    35. if (cur == NULL) //cur == NULL代表着phead指向NULL, 这时候让phead改变指向。传送phead指针的原因就是
    36. { //要改变phead的指向。
    37. *pphead = newnode;//
    38. }
    39. else
    40. {
    41. while (cur->next != NULL) //让cur遍历到最后一个节点
    42. {
    43. cur = cur->next;
    44. }
    45. cur->next = newnode;//最后
    46. }
    47. }

    尾插接口时传送phead的指针的原因是因为phead可能改变指向,从空指针变为指向一个节点。要改变phead的指向那就是意味着形参要相对于phead传址调用,  而phead本身就是一个一级指针, phead取地址就是一个二级指针, 所以形参是二级指针。

    ---------------------------------------------------------------------------------------------------------------------------------

    单链表头插函数接口

    .h函数接口

    1. 链表的增删查改///
    2. #include
    3. #include
    4. #include
    5. typedef int SLTDataType;
    6. typedef struct SListNode
    7. {
    8. struct SListNode* next;
    9. SLTDataType data;
    10. }SLNode;
    11. //链表接口声明/
    12. //申请链表节点函数接口
    13. SLNode* BuySListNode(SLTDataType x);
    14. //单链表的打印函数接口
    15. void SListPrint(SLNode* phead);
    16. //单链表尾插函数接口
    17. void SListPushBack(SLNode** pphead, SLTDataType x);
    18. //单链表头插函数接口
    19. void SListPushFront(SLNode** pphead, SLTDataType x);

    .c函数实现

    1. 单链表函数实现函数接口
    2. //单链表申请节点函数接口
    3. SLNode* BuySListNode(SLTDataType x)
    4. {
    5. SLNode* NewNode = (SLNode*)malloc(sizeof(SLNode));
    6. if (NewNode == NULL)
    7. {
    8. printf("申请节点失败\n");
    9. return;
    10. }
    11. //
    12. NewNode->data = x;
    13. NewNode->next = NULL;
    14. }
    15. //单链表的节点打印操作
    16. void SListPrint(SLNode* phead)
    17. {
    18. SLNode* cur = phead;
    19. while (cur != NULL)
    20. {
    21. printf("%d->", cur->data);
    22. cur = cur->next;
    23. }
    24. if (cur == NULL)//最后打印一个NULL
    25. {
    26. printf("NULL");
    27. }
    28. }
    29. //单链表尾插函数接口
    30. void SListPushBack(SLNode** pphead, SLTDataType x)
    31. {
    32. assert(pphead);
    33. SLNode* newnode = BuySListNode(x);//利用申请节点函数申请节点
    34. SLNode* cur = *pphead; //让cur指向phead所指向空间
    35. if (cur == NULL) //cur == NULL代表着phead指向NULL, 这时候让phead改变指向。传送phead指针的原因就是
    36. { //要改变phead的指向。
    37. *pphead = newnode;//
    38. }
    39. else
    40. {
    41. while (cur->next != NULL) //让cur遍历到最后一个节点
    42. {
    43. cur = cur->next;
    44. }
    45. cur->next = newnode;//最后
    46. }
    47. }
    48. //单链表头插函数接口
    49. void SListPushFront(SLNode** pphead, SLTDataType x)
    50. {
    51. assert(pphead);
    52. SLNode* newnode = BuySListNode(x);
    53. //
    54. SLNode* cur = *pphead;
    55. newnode->next = cur;
    56. *pphead = newnode;
    57. }

    现在我们来利用打印接口调试一下我们写的是否存在问题。 

    在main.c中输入如下代码

    1. void TestSListNode()
    2. {
    3. SLNode* phead = NULL;
    4. SListPushBack(&phead, 1);
    5. SListPushBack(&phead, 2);
    6. SListPushBack(&phead, 3);
    7. SListPushBack(&phead, 4);
    8. SListPushBack(&phead, 5);
    9. SListPushFront(&phead, 0);
    10. SListPrint(phead);
    11. printf("\n");
    12. /*SListPopFront(&phead);
    13. SListPopFront(&phead);
    14. SListPopFront(&phead);
    15. SListPopBack(&phead);
    16. SListPrint(phead);
    17. printf("\n");
    18. SLTDestory(&phead);
    19. SListPrint(phead);
    20. printf("\n");*/
    21. }
    22. int main()
    23. {
    24. // TestTree();
    25. // TestStack();
    26. // TestQueue();
    27. // TestSeqList();
    28. TestSListNode();
    29. // TestDSLNode();
    30. // TestOJ();
    31. return 0;
    32. }

    运行图如下: 

     通过检验,没有问题。 继续往下走。 

    ---------------------------------------------------------------------------------------------------------------------------------

     单链表尾删函数接口

    .h文件声明

    1. 链表的增删查改///
    2. #include
    3. #include
    4. #include
    5. typedef int SLTDataType;
    6. typedef struct SListNode
    7. {
    8. struct SListNode* next;
    9. SLTDataType data;
    10. }SLNode;
    11. //链表接口声明/
    12. //申请链表节点函数接口
    13. SLNode* BuySListNode(SLTDataType x);
    14. //单链表的打印函数接口
    15. void SListPrint(SLNode* phead);
    16. //单链表尾插函数接口
    17. void SListPushBack(SLNode** pphead, SLTDataType x);
    18. //单链表头插函数接口
    19. void SListPushFront(SLNode** pphead, SLTDataType x);
    20. //单链表尾删函数接口
    21. void SListPopBack(SLNode** pphead);

    .c函数实现

      首先pphead不能为空, 如果phead指向空的话就直接返回。 然后定义cur和prev两个指针, 遍历寻找尾节点。 cur领先prev一个节点, cur指向尾节点的时候, 就释放掉这个节点。 然后prev指向空节点。 寻找尾节点的过程是这样的:

    代码实现

    1. 单链表函数实现函数接口
    2. //单链表申请节点函数接口
    3. SLNode* BuySListNode(SLTDataType x)
    4. {
    5. SLNode* NewNode = (SLNode*)malloc(sizeof(SLNode));
    6. if (NewNode == NULL)
    7. {
    8. printf("申请节点失败\n");
    9. return;
    10. }
    11. //
    12. NewNode->data = x;
    13. NewNode->next = NULL;
    14. }
    15. //单链表的节点打印操作
    16. void SListPrint(SLNode* phead)
    17. {
    18. SLNode* cur = phead;
    19. while (cur != NULL)
    20. {
    21. printf("%d->", cur->data);
    22. cur = cur->next;
    23. }
    24. if (cur == NULL)//最后打印一个NULL
    25. {
    26. printf("NULL");
    27. }
    28. }
    29. //单链表尾插函数接口
    30. void SListPushBack(SLNode** pphead, SLTDataType x)
    31. {
    32. assert(pphead);
    33. SLNode* newnode = BuySListNode(x);//利用申请节点函数申请节点
    34. SLNode* cur = *pphead; //让cur指向phead所指向空间
    35. if (cur == NULL) //cur == NULL代表着phead指向NULL, 这时候让phead改变指向。传送phead指针的原因就是
    36. { //要改变phead的指向。
    37. *pphead = newnode;//
    38. }
    39. else
    40. {
    41. while (cur->next != NULL) //让cur遍历到最后一个节点
    42. {
    43. cur = cur->next;
    44. }
    45. cur->next = newnode;//最后
    46. }
    47. }
    48. //单链表头插函数接口
    49. void SListPushFront(SLNode** pphead, SLTDataType x)
    50. {
    51. assert(pphead);
    52. SLNode* newnode = BuySListNode(x);
    53. //
    54. SLNode* cur = *pphead;
    55. newnode->next = cur;
    56. *pphead = newnode;
    57. }
    58. //单链表尾删函数接口
    59. void SListPopBack(SLNode** pphead)
    60. {
    61. assert(pphead);
    62. if (*pphead == NULL)
    63. return;
    64. //
    65. SLNode* cur = *pphead;
    66. SLNode* prev = *pphead;
    67. while (cur->next != NULL) //对链表进行遍历。 cur最终会指向尾节点。 prev用来维护链表
    68. {
    69. prev = cur;
    70. cur = cur->next;
    71. }
    72. if (prev == cur)
    73. {
    74. free(cur);
    75. *pphead = NULL;
    76. }
    77. else
    78. {
    79. free(cur);
    80. prev = NULL;
    81. }
    82. }

    ---------------------------------------------------------------------------------------------------------------------------------

    单链表的头删函数接口

    .h函数声明

    1. 链表的增删查改///
    2. #include
    3. #include
    4. #include
    5. typedef int SLTDataType;
    6. typedef struct SListNode
    7. {
    8. struct SListNode* next;
    9. SLTDataType data;
    10. }SLNode;
    11. //链表接口声明/
    12. //申请链表节点函数接口
    13. SLNode* BuySListNode(SLTDataType x);
    14. //单链表的打印函数接口
    15. void SListPrint(SLNode* phead);
    16. //单链表尾插函数接口
    17. void SListPushBack(SLNode** pphead, SLTDataType x);
    18. //单链表头插函数接口
    19. void SListPushFront(SLNode** pphead, SLTDataType x);
    20. //单链表尾删函数接口
    21. void SListPopBack(SLNode** pphead);
    22. //单链表的头删函数接口
    23. void SListPopFront(SLNode** pphead);

    .c函数实现 

    1. 单链表函数实现函数接口
    2. //单链表申请节点函数接口
    3. SLNode* BuySListNode(SLTDataType x)
    4. {
    5. SLNode* NewNode = (SLNode*)malloc(sizeof(SLNode));
    6. if (NewNode == NULL)
    7. {
    8. printf("申请节点失败\n");
    9. return;
    10. }
    11. //
    12. NewNode->data = x;
    13. NewNode->next = NULL;
    14. }
    15. //单链表的节点打印操作
    16. void SListPrint(SLNode* phead)
    17. {
    18. SLNode* cur = phead;
    19. while (cur != NULL)
    20. {
    21. printf("%d->", cur->data);
    22. cur = cur->next;
    23. }
    24. if (cur == NULL)//最后打印一个NULL
    25. {
    26. printf("NULL");
    27. }
    28. }
    29. //单链表尾插函数接口
    30. void SListPushBack(SLNode** pphead, SLTDataType x)
    31. {
    32. assert(pphead);
    33. SLNode* newnode = BuySListNode(x);//利用申请节点函数申请节点
    34. SLNode* cur = *pphead; //让cur指向phead所指向空间
    35. if (cur == NULL) //cur == NULL代表着phead指向NULL, 这时候让phead改变指向。传送phead指针的原因就是
    36. { //要改变phead的指向。
    37. *pphead = newnode;//
    38. }
    39. else
    40. {
    41. while (cur->next != NULL) //让cur遍历到最后一个节点
    42. {
    43. cur = cur->next;
    44. }
    45. cur->next = newnode;//最后
    46. }
    47. }
    48. //单链表头插函数接口
    49. void SListPushFront(SLNode** pphead, SLTDataType x)
    50. {
    51. assert(pphead);
    52. SLNode* newnode = BuySListNode(x);
    53. //
    54. SLNode* cur = *pphead;
    55. newnode->next = cur;
    56. *pphead = newnode;
    57. }
    58. //单链表尾删函数接口
    59. void SListPopBack(SLNode** pphead)
    60. {
    61. assert(pphead);
    62. if (*pphead == NULL)
    63. return;
    64. //
    65. SLNode* cur = *pphead;
    66. SLNode* prev = *pphead;
    67. while (cur->next != NULL) //对链表进行遍历。 cur最终会指向尾节点。 prev用来维护链表
    68. {
    69. prev = cur;
    70. cur = cur->next;
    71. }
    72. if (prev == cur)
    73. {
    74. free(cur);
    75. *pphead = NULL;
    76. }
    77. else
    78. {
    79. free(cur);
    80. prev = NULL;
    81. }
    82. }
    83. //单链表的头删函数接口
    84. void SListPopFront(SLNode** pphead)
    85. {
    86. assert(pphead);
    87. if (*pphead == NULL)
    88. return;
    89. //
    90. SLNode* cur = *pphead;
    91. *pphead = cur->next;
    92. free(cur);
    93. }

    代码的意思是, 首先pphead不能为空, 然后phead不能指向空。 然后让一个cur指针指向头节点。 然后修改phead的指向, 使其指向第二个节点(当第二个节点是空的时候, 就是指向空)。然后释放cur指向的节点也就是头节点。 如图为过程:

    ---------------------------------------------------------------------------------------------------------------------------------

     单链表查找函数接口

    .h接口声明

    1. 链表的增删查改///
    2. #include
    3. #include
    4. #include
    5. typedef int SLTDataType;
    6. typedef struct SListNode
    7. {
    8. struct SListNode* next;
    9. SLTDataType data;
    10. }SLNode;
    11. //链表接口声明/
    12. //申请链表节点函数接口
    13. SLNode* BuySListNode(SLTDataType x);
    14. //单链表的打印函数接口
    15. void SListPrint(SLNode* phead);
    16. //单链表尾插函数接口
    17. void SListPushBack(SLNode** pphead, SLTDataType x);
    18. //单链表头插函数接口
    19. void SListPushFront(SLNode** pphead, SLTDataType x);
    20. //单链表尾删函数接口
    21. void SListPopBack(SLNode** pphead);
    22. //单链表的头删函数接口
    23. void SListPopFront(SLNode** pphead);
    24. //单链表查找函数接口
    25. SLNode* SListFind(SLNode* phead, SLTDataType x);

    .c接口实现

    1. 单链表函数实现函数接口
    2. //单链表申请节点函数接口
    3. SLNode* BuySListNode(SLTDataType x)
    4. {
    5. SLNode* NewNode = (SLNode*)malloc(sizeof(SLNode));
    6. if (NewNode == NULL)
    7. {
    8. printf("申请节点失败\n");
    9. return;
    10. }
    11. //
    12. NewNode->data = x;
    13. NewNode->next = NULL;
    14. }
    15. //单链表的节点打印操作
    16. void SListPrint(SLNode* phead)
    17. {
    18. SLNode* cur = phead;
    19. while (cur != NULL)
    20. {
    21. printf("%d->", cur->data);
    22. cur = cur->next;
    23. }
    24. if (cur == NULL)//最后打印一个NULL
    25. {
    26. printf("NULL");
    27. }
    28. }
    29. //单链表尾插函数接口
    30. void SListPushBack(SLNode** pphead, SLTDataType x)
    31. {
    32. assert(pphead);
    33. SLNode* newnode = BuySListNode(x);//利用申请节点函数申请节点
    34. SLNode* cur = *pphead; //让cur指向phead所指向空间
    35. if (cur == NULL) //cur == NULL代表着phead指向NULL, 这时候让phead改变指向。传送phead指针的原因就是
    36. { //要改变phead的指向。
    37. *pphead = newnode;//
    38. }
    39. else
    40. {
    41. while (cur->next != NULL) //让cur遍历到最后一个节点
    42. {
    43. cur = cur->next;
    44. }
    45. cur->next = newnode;//最后
    46. }
    47. }
    48. //单链表头插函数接口
    49. void SListPushFront(SLNode** pphead, SLTDataType x)
    50. {
    51. assert(pphead);
    52. SLNode* newnode = BuySListNode(x);
    53. //
    54. SLNode* cur = *pphead;
    55. newnode->next = cur;
    56. *pphead = newnode;
    57. }
    58. //单链表尾删函数接口
    59. void SListPopBack(SLNode** pphead)
    60. {
    61. assert(pphead);
    62. if (*pphead == NULL)
    63. return;
    64. //
    65. SLNode* cur = *pphead;
    66. SLNode* prev = *pphead;
    67. while (cur->next != NULL) //对链表进行遍历。 cur最终会指向尾节点。 prev用来维护链表
    68. {
    69. prev = cur;
    70. cur = cur->next;
    71. }
    72. if (prev == cur)
    73. {
    74. free(cur);
    75. *pphead = NULL;
    76. }
    77. else
    78. {
    79. free(cur);
    80. prev = NULL;
    81. }
    82. }
    83. //单链表的头删函数接口
    84. void SListPopFront(SLNode** pphead)
    85. {
    86. assert(pphead);
    87. if (*pphead == NULL)
    88. return;
    89. //
    90. SLNode* cur = *pphead;
    91. *pphead = cur->next;
    92. free(cur);
    93. }
    94. //单链表查找函数接口
    95. SLNode* SListFind(SLNode* phead, SLTDataType x)
    96. {
    97. SLNode* cur = phead;//定义一个指向头节点的指针, 用于遍历
    98. while (cur != NULL) //向后遍历
    99. {
    100. if (cur->data == x) //找到节点后就返回节点的地址。
    101. {
    102. return cur;
    103. }
    104. cur = cur->next;
    105. }
    106. return NULL;
    107. }

     代码太长, 之后.c文件的代码只展示相应接口的代码

    ---------------------------------------------------------------------------------------------------------------------------------

    单链表pos位置之后插入数据接口

    .h接口声明

    1. 链表的增删查改///
    2. #include
    3. #include
    4. #include
    5. typedef int SLTDataType;
    6. typedef struct SListNode
    7. {
    8. struct SListNode* next;
    9. SLTDataType data;
    10. }SLNode;
    11. //链表接口声明/
    12. //申请链表节点函数接口
    13. SLNode* BuySListNode(SLTDataType x);
    14. //单链表的打印函数接口
    15. void SListPrint(SLNode* phead);
    16. //单链表尾插函数接口
    17. void SListPushBack(SLNode** pphead, SLTDataType x);
    18. //单链表头插函数接口
    19. void SListPushFront(SLNode** pphead, SLTDataType x);
    20. //单链表尾删函数接口
    21. void SListPopBack(SLNode** pphead);
    22. //单链表的头删函数接口
    23. void SListPopFront(SLNode** pphead);
    24. //单链表查找函数接口
    25. SLNode* SListFind(SLNode* phead, SLTDataType x);
    26. //单链表在pos位置之后插入数据接口
    27. void SListInsertAfter(SLNode* pos, SLTDataType x);

    .c接口实现 

    1. //单链表在pos位置之后插入数据接口
    2. void SListInsertAfter(SLNode* pos, SLTDataType x)
    3. {
    4. assert(pos);
    5. SLNode* newnode = BuySListNode(x);
    6. //
    7. SLNode* cur = pos->next;
    8. newnode->next = cur;
    9. pos->next = newnode;
    10. }

     该接口的实现过程如下:

    令指针cur指向pos的下一个节点, newnode的next指向cur, pos的next指向newnode

    ---------------------------------------------------------------------------------------------------------------------------------

    单链表删除pos之后位置的数据

    .h接口声明

    1. 链表的增删查改///
    2. #include
    3. #include
    4. #include
    5. typedef int SLTDataType;
    6. typedef struct SListNode
    7. {
    8. struct SListNode* next;
    9. SLTDataType data;
    10. }SLNode;
    11. //链表接口声明/
    12. //申请链表节点函数接口
    13. SLNode* BuySListNode(SLTDataType x);
    14. //单链表的打印函数接口
    15. void SListPrint(SLNode* phead);
    16. //单链表尾插函数接口
    17. void SListPushBack(SLNode** pphead, SLTDataType x);
    18. //单链表头插函数接口
    19. void SListPushFront(SLNode** pphead, SLTDataType x);
    20. //单链表尾删函数接口
    21. void SListPopBack(SLNode** pphead);
    22. //单链表的头删函数接口
    23. void SListPopFront(SLNode** pphead);
    24. //单链表查找函数接口
    25. SLNode* SListFind(SLNode* phead, SLTDataType x);
    26. //单链表在pos位置之后插入数据接口
    27. void SListInsertAfter(SLNode* pos, SLTDataType x);
    28. //单链表在pos之后的位置删除数据
    29. void SListPopAfter(SLNode* pos);

    .c接口实现

    1. //单链表在pos之后的位置删除数据
    2. void SListPopAfter(SLNode* pos)
    3. {
    4. assert(pos);
    5. //
    6. SLNode* cur = pos->next;
    7. pos->next = pos->next->next;
    8. free(cur);
    9. }

    该接口实现和单链表在pos位置之后插入数据接口实现方式类似, 都是利用一个cur指针指向pos之后的位置作为记忆保存,然后进行插入或者删除操作。 

    ---------------------------------------------------------------------------------------------------------------------------------

    单链表在pos位置之前插入数据接口

    该接口的实现有点复杂, 但是实现该接口之后, 对于尾插还有头插就很好实现了, 尾插和头插是该接口的两个特殊情况。 假如pos是头节点,就是头插, 假如pos是尾节点, 就是尾插。

    .h接口声明

    1. 链表的增删查改///
    2. #include
    3. #include
    4. #include
    5. typedef int SLTDataType;
    6. typedef struct SListNode
    7. {
    8. struct SListNode* next;
    9. SLTDataType data;
    10. }SLNode;
    11. //链表接口声明/
    12. //申请链表节点函数接口
    13. SLNode* BuySListNode(SLTDataType x);
    14. //单链表的打印函数接口
    15. void SListPrint(SLNode* phead);
    16. //单链表尾插函数接口
    17. void SListPushBack(SLNode** pphead, SLTDataType x);
    18. //单链表头插函数接口
    19. void SListPushFront(SLNode** pphead, SLTDataType x);
    20. //单链表尾删函数接口
    21. void SListPopBack(SLNode** pphead);
    22. //单链表的头删函数接口
    23. void SListPopFront(SLNode** pphead);
    24. //单链表查找函数接口
    25. SLNode* SListFind(SLNode* phead, SLTDataType x);
    26. //单链表在pos位置之后插入数据接口
    27. void SListInsertAfter(SLNode* pos, SLTDataType x);
    28. //单链表在pos之后的位置删除数据
    29. void SListPopAfter(SLNode* pos);
    30. //单链表在pos位置之前插入数据接口
    31. void SListInsert(SLNode** pphead, SLNode* pos, SLTDataType x);

    .c接口实现

    1. //单链表在pos位置之前插入数据接口
    2. void SListInsert(SLNode** pphead, SLNode* pos, SLTDataType x)
    3. {
    4. assert(pphead);
    5. //
    6. SLNode* newnode = BuySListNode(x);
    7. //
    8. if (*pphead == NULL)
    9. {
    10. *pphead = newnode;
    11. return;
    12. }
    13. //
    14. if (*pphead == pos)
    15. {
    16. newnode->next = pos;
    17. *pphead = newnode;
    18. return;
    19. }
    20. SLNode* cur = *pphead;
    21. while (cur != NULL && cur->next != pos)
    22. {
    23. cur = cur->next;
    24. }
    25. newnode->next = pos;
    26. cur->next = newnode;
    27. }

    该接口分为三种情况:

    第一种是链表为空, 这个时候直接插入节点。

    第二种情况是pos的位置在第一个节点的位置, 这个时候需要改变phead的指向。 

    第三种情况就是最普通的情况, 在除头节点, 链表的任意节点前插入。如图:

     

    ---------------------------------------------------------------------------------------------------------------------------------

    单链表删除pos位置数据接口

    和pos位置插入数据接口一样, 实现了该接口对于尾删, 头删接口就很简单了。 头删, 尾删都是单链表删除pos位置数据接口的特殊情况。 pos是头节点, 就是头删, pos是尾节点。 就是尾删。 

    .h接口声明

    1. 链表的增删查改///
    2. #include
    3. #include
    4. #include
    5. typedef int SLTDataType;
    6. typedef struct SListNode
    7. {
    8. struct SListNode* next;
    9. SLTDataType data;
    10. }SLNode;
    11. //链表接口声明/
    12. //申请链表节点函数接口
    13. SLNode* BuySListNode(SLTDataType x);
    14. //单链表的打印函数接口
    15. void SListPrint(SLNode* phead);
    16. //单链表尾插函数接口
    17. void SListPushBack(SLNode** pphead, SLTDataType x);
    18. //单链表头插函数接口
    19. void SListPushFront(SLNode** pphead, SLTDataType x);
    20. //单链表尾删函数接口
    21. void SListPopBack(SLNode** pphead);
    22. //单链表的头删函数接口
    23. void SListPopFront(SLNode** pphead);
    24. //单链表查找函数接口
    25. SLNode* SListFind(SLNode* phead, SLTDataType x);
    26. //单链表在pos位置之后插入数据接口
    27. void SListInsertAfter(SLNode* pos, SLTDataType x);
    28. //单链表在pos之后的位置删除数据
    29. void SListPopAfter(SLNode* pos);
    30. //单链表在pos位置之前插入数据接口
    31. void SListInsert(SLNode** pphead, SLNode* pos, SLTDataType x);
    32. //单链表在pos位置删除数据接口
    33. void SListPop(SLNode** pphead, SLNode* pos);

    .c接口实现

    1. //单链表在pos位置删除数据接口
    2. void SListPop(SLNode** pphead, SLNode* pos)
    3. {
    4. assert(pphead);
    5. //
    6. if (*pphead == pos)
    7. {
    8. *pphead = (*pphead)->next;
    9. free(pos);
    10. }
    11. else
    12. {
    13. SLNode* cur = *pphead;
    14. while (cur->next != pos)
    15. {
    16. cur->next = pos->next;
    17. free(pos);
    18. }
    19. }
    20. }

    pos位置删除分两种情况

    一种是头删, 需要phead改变指向。

     一种是其他位置删除节点

    单链表的销毁

     链表使用完之后应该销毁, 放置内存泄漏

    .h接口声明

    1. 链表的增删查改///
    2. #include
    3. #include
    4. #include
    5. typedef int SLTDataType;
    6. typedef struct SListNode
    7. {
    8. struct SListNode* next;
    9. SLTDataType data;
    10. }SLNode;
    11. //链表接口声明/
    12. //申请链表节点函数接口
    13. SLNode* BuySListNode(SLTDataType x);
    14. //单链表的打印函数接口
    15. void SListPrint(SLNode* phead);
    16. //单链表尾插函数接口
    17. void SListPushBack(SLNode** pphead, SLTDataType x);
    18. //单链表头插函数接口
    19. void SListPushFront(SLNode** pphead, SLTDataType x);
    20. //单链表尾删函数接口
    21. void SListPopBack(SLNode** pphead);
    22. //单链表的头删函数接口
    23. void SListPopFront(SLNode** pphead);
    24. //单链表查找函数接口
    25. SLNode* SListFind(SLNode* phead, SLTDataType x);
    26. //单链表在pos位置之后插入数据接口
    27. void SListInsertAfter(SLNode* pos, SLTDataType x);
    28. //单链表在pos之后的位置删除数据
    29. void SListPopAfter(SLNode* pos);
    30. //单链表在pos位置之前插入数据接口
    31. void SListInsert(SLNode** pphead, SLNode* pos, SLTDataType x);
    32. //单链表在pos位置删除数据接口
    33. void SListPop(SLNode** pphead, SLNode* pos);
    34. //单链表的销毁函数接口
    35. void SLTDestory(SLNode** pphead);

    .c接口实现 

    1. //单链表的销毁函数接口
    2. void SLTDestory(SLNode** pphead)
    3. {
    4. assert(pphead);
    5. if (*pphead == NULL)
    6. {
    7. return;
    8. }
    9. SLNode* prev = (*pphead);
    10. SLNode* cur = (*pphead)->next;
    11. if (cur == NULL)
    12. {
    13. *pphead = NULL;
    14. free(prev);
    15. }
    16. else
    17. {
    18. *pphead = NULL;
    19. while(cur != NULL)
    20. {
    21. free(prev);
    22. prev = cur;
    23. cur = cur->next;
    24. }
    25. free(prev);
    26. }
    27. }

    销毁需要一步一步的进行, 如下图:

    现在来看一下总体代码:

    .h文件

    1. 链表的增删查改///
    2. #include
    3. #include
    4. #include
    5. typedef int SLTDataType;
    6. typedef struct SListNode
    7. {
    8. struct SListNode* next;
    9. SLTDataType data;
    10. }SLNode;
    11. //链表接口声明/
    12. //申请链表节点函数接口
    13. SLNode* BuySListNode(SLTDataType x);
    14. //单链表的打印函数接口
    15. void SListPrint(SLNode* phead);
    16. //单链表尾插函数接口
    17. void SListPushBack(SLNode** pphead, SLTDataType x);
    18. //单链表头插函数接口
    19. void SListPushFront(SLNode** pphead, SLTDataType x);
    20. //单链表尾删函数接口
    21. void SListPopBack(SLNode** pphead);
    22. //单链表的头删函数接口
    23. void SListPopFront(SLNode** pphead);
    24. //单链表查找函数接口
    25. SLNode* SListFind(SLNode* phead, SLTDataType x);
    26. //单链表在pos位置之后插入数据接口
    27. void SListInsertAfter(SLNode* pos, SLTDataType x);
    28. //单链表在pos之后的位置删除数据
    29. void SListPopAfter(SLNode* pos);
    30. //单链表在pos位置之前插入数据接口
    31. void SListInsert(SLNode** pphead, SLNode* pos, SLTDataType x);
    32. //单链表在pos位置删除数据接口
    33. void SListPop(SLNode** pphead, SLNode* pos);
    34. //单链表的销毁函数接口
    35. void SLTDestory(SLNode** pphead);

    .c文件

    1. 单链表函数实现函数接口
    2. //单链表申请节点函数接口
    3. SLNode* BuySListNode(SLTDataType x)
    4. {
    5. SLNode* NewNode = (SLNode*)malloc(sizeof(SLNode));
    6. if (NewNode == NULL)
    7. {
    8. printf("申请节点失败\n");
    9. return;
    10. }
    11. //
    12. NewNode->data = x;
    13. NewNode->next = NULL;
    14. }
    15. //单链表的节点打印操作
    16. void SListPrint(SLNode* phead)
    17. {
    18. SLNode* cur = phead;
    19. while (cur != NULL)
    20. {
    21. printf("%d->", cur->data);
    22. cur = cur->next;
    23. }
    24. if (cur == NULL)//最后打印一个NULL
    25. {
    26. printf("NULL");
    27. }
    28. }
    29. //单链表尾插函数接口
    30. void SListPushBack(SLNode** pphead, SLTDataType x)
    31. {
    32. assert(pphead);
    33. SLNode* newnode = BuySListNode(x);//利用申请节点函数申请节点
    34. SLNode* cur = *pphead; //让cur指向phead所指向空间
    35. if (cur == NULL) //cur == NULL代表着phead指向NULL, 这时候让phead改变指向。传送phead指针的原因就是
    36. { //要改变phead的指向。
    37. *pphead = newnode;//
    38. }
    39. else
    40. {
    41. while (cur->next != NULL) //让cur遍历到最后一个节点
    42. {
    43. cur = cur->next;
    44. }
    45. cur->next = newnode;//最后
    46. }
    47. }
    48. //单链表头插函数接口
    49. void SListPushFront(SLNode** pphead, SLTDataType x)
    50. {
    51. assert(pphead);
    52. SLNode* newnode = BuySListNode(x);
    53. //
    54. SLNode* cur = *pphead;
    55. newnode->next = cur;
    56. *pphead = newnode;
    57. }
    58. //单链表尾删函数接口
    59. void SListPopBack(SLNode** pphead)
    60. {
    61. assert(pphead);
    62. if (*pphead == NULL)
    63. return;
    64. //
    65. SLNode* cur = *pphead;
    66. SLNode* prev = *pphead;
    67. while (cur->next != NULL) //对链表进行遍历。 cur最终会指向尾节点。 prev用来维护链表
    68. {
    69. prev = cur;
    70. cur = cur->next;
    71. }
    72. if (prev == cur)
    73. {
    74. free(cur);
    75. *pphead = NULL;
    76. }
    77. else
    78. {
    79. free(cur);
    80. prev = NULL;
    81. }
    82. }
    83. //单链表的头删函数接口
    84. void SListPopFront(SLNode** pphead)
    85. {
    86. assert(pphead);
    87. if (*pphead == NULL)
    88. return;
    89. //
    90. SLNode* cur = *pphead;
    91. *pphead = cur->next;
    92. free(cur);
    93. }
    94. //单链表查找函数接口
    95. SLNode* SListFind(SLNode* phead, SLTDataType x)
    96. {
    97. SLNode* cur = phead;//定义一个指向头节点的指针, 用于遍历
    98. while (cur != NULL) //向后遍历
    99. {
    100. if (cur->data == x) //找到节点后就返回节点的地址。
    101. {
    102. return cur;
    103. }
    104. cur = cur->next;
    105. }
    106. return NULL;
    107. }
    108. //单链表在pos位置之后插入数据接口
    109. void SListInsertAfter(SLNode* pos, SLTDataType x)
    110. {
    111. assert(pos);
    112. SLNode* newnode = BuySListNode(x);
    113. //
    114. SLNode* cur = pos->next;
    115. newnode->next = cur;
    116. pos->next = newnode;
    117. }
    118. //单链表在pos之后的位置删除数据
    119. void SListPopAfter(SLNode* pos)
    120. {
    121. assert(pos);
    122. //
    123. SLNode* cur = pos->next;
    124. pos->next = pos->next->next;
    125. free(cur);
    126. }
    127. //单链表在pos位置之前插入数据接口
    128. void SListInsert(SLNode** pphead, SLNode* pos, SLTDataType x)
    129. {
    130. assert(pphead);
    131. //
    132. SLNode* newnode = BuySListNode(x);
    133. //
    134. if (*pphead == NULL)
    135. {
    136. *pphead = newnode;
    137. return;
    138. }
    139. //
    140. if (*pphead == pos)
    141. {
    142. newnode->next = pos;
    143. *pphead = newnode;
    144. return;
    145. }
    146. SLNode* cur = *pphead;
    147. while (cur != NULL && cur->next != pos)
    148. {
    149. cur = cur->next;
    150. }
    151. newnode->next = pos;
    152. cur->next = newnode;
    153. }
    154. //单链表在pos位置删除数据接口
    155. void SListPop(SLNode** pphead, SLNode* pos)
    156. {
    157. assert(pphead);
    158. //
    159. if (*pphead == pos)
    160. {
    161. *pphead = (*pphead)->next;
    162. free(pos);
    163. }
    164. else
    165. {
    166. SLNode* cur = *pphead;
    167. while (cur->next != pos)
    168. {
    169. cur->next = pos->next;
    170. free(pos);
    171. }
    172. }
    173. }
    174. //单链表的销毁函数接口
    175. void SLTDestory(SLNode** pphead)
    176. {
    177. assert(pphead);
    178. if (*pphead == NULL)
    179. {
    180. return;
    181. }
    182. SLNode* prev = (*pphead);
    183. SLNode* cur = (*pphead)->next;
    184. if (cur == NULL)
    185. {
    186. *pphead = NULL;
    187. free(prev);
    188. }
    189. else
    190. {
    191. *pphead = NULL;
    192. while(cur != NULL)
    193. {
    194. free(prev);
    195. prev = cur;
    196. cur = cur->next;
    197. }
    198. free(prev);
    199. }
    200. }

  • 相关阅读:
    MySQL 8.0 Clone Plugin 详解
    skywalking--链路追踪
    31.JavaScript数组进阶,一网打尽数组操作函数slice、filter、map、reduce、some、every、find、splice
    Allatori工具混淆jar包class
    基于图搜索的规划算法之 A* 家族(八):Theta* 算法
    Python之作业(一)
    Machine Learning(一)KNN算法
    测试 C、Python、Java 等 16 种编程语言的 Hello World:7 种存在 Bug?
    若依3.6.0使用Mybatis-plus分页失效以及完美替换Pagehelper【已解决】
    HarmonyOS入门开发(二) Toast使用
  • 原文地址:https://blog.csdn.net/strive_mianyang/article/details/136751789