当前位置:   article > 正文

【数据结构】单链表的基本操作(C语言版)_单链表的创建:分配空间、参数初始化 (3)单链表的插入:创建新节点,在位置i-1和i之

单链表的创建:分配空间、参数初始化 (3)单链表的插入:创建新节点,在位置i-1和i之

目录

单链表的定义

 单链表的特点以及与顺序表的差别

单链表的基本操作 

1、单链表的初始化

 2、头插法建立单链表

3、尾插法建立单链表 

 4、求单链表长度

5、按值查找元素

6、按序号查找元素

7、在第i个位置前插入节点(元素)

8、在某个值x前插入节点 

 9、按位置删除节点

 10、删除单链表中所有数据域等于x的节点

11、输出单链表数据

12、销毁单链表 

完整测试代码: 


单链表的定义

单链表(Singly linked list)是一种常见的数据结构,它由一个结点(Node)的链表构成,每个结点包含两个域:数据域和指针域。数据域用于存储结点的数据,指针域用于存储下一个结点的地址。

单链表中的第一个结点被称为头结点(Head),它用来存放链表的基地址。头结点中的指针域指向链表的第一个结点,最后一个结点的指针域指向空地址,表示链表结束。

下面是一个单链表结点的定义:

  1. typedef int DataType;
  2. typedef struct ListNode
  3. {
  4. DataType data; // 数据域,存储结点的数据
  5. struct ListNode* next; // 指针域,指向下一个结点
  6. }ListNode;

 

值得注意的是,单链表是一种动态数据结构,可以在运行时动态添加或删除结点,也可以在任何位置插入或删除结点,因为每个结点的指针域储存的是下一个结点的地址,所以链表的结构是相对灵活的,有着较强的扩展性。

 单链表的特点以及与顺序表的差别

  1. 链表是动态数据结构:链表的长度可以根据实际需要进行动态调整,可以在运行时添加或删除结点。

  2. 链表的插入和删除操作高效:在链表中插入或删除结点的操作时间复杂度为O(1),只需要修改指针的指向即可,而不需要像数组那样需要移动其他元素。

  3. 链表对内存的利用率较低:链表中的结点是通过指针连接的,需要额外的内存空间来存储指针。

  4. 链表的访问操作相对较慢:由于链表中的结点不是连续存储的,因此无法像数组一样通过下标直接访问元素,需要从头结点开始遍历链表,直到找到目标结点。

  5. 链表的结构灵活:可以在链表中间插入或删除结点,只需修改指针的指向即可。而数组的长度是固定的,插入或删除元素需要移动其他元素。

  6. 链表适用于频繁插入和删除的场景:由于链表插入和删除的时间复杂度为O(1),相比数组,链表更适合频繁插入和删除结点的情况。

需要注意的是,链表的访问操作时间复杂度为O(n),其中n是链表的长度。因此,在需要频繁根据索引访问元素的情况下,数组可能更加合适。而链表适用于需要动态调整长度、频繁插入和删除元素的场景。


单链表的基本操作 

1、单链表的初始化

 单链表的初始化即构造一个仅包含头结点的空单链表。其过程是首先申请一个结点并让指针 head指向该结点,然后将它的指针域赋为空(NULL),最后返回头指针 head。

首先我们先了解下什么是头节点以及为什么要设置头节点:

  1. 方便操作空链表:初始时,链表是空的,没有任何结点。如果没有头结点,链表的第一个结点就是空链表。通过设置头结点,即使链表为空,也有了一个起始的位置,可以方便地进行插入和删除操作。

  2. 简化插入和删除操作:有了头结点,插入和删除操作可以统一处理。无论在链表的头部、中间还是尾部插入或删除结点,操作逻辑都是一致的。通过将头结点作为起点,可以避免处理边界情况的复杂性。

  3. 方便获取链表长度:头结点为链表提供了一个便捷的起点,从头结点开始遍历链表,可以方便地计算链表的长度。

  4. 方便遍历操作:通过头结点,从链表的第一个结点开始遍历整个链表,可以避免操作和管理指针时的边界情况。

注意:头结点不存储实际的数据,仅作为链表的起点其下一个结点指向链表的第一个有效节点。因此,在使用链表时,需要特别注意头结点的存在,并在涉及到插入、删除和遍历操作时进行处理。

由此可见,设置头节点可以简化部分操作。当然不设置头节点也是可以的,但是博主建议大家在学习单链表的初期尽量使用带头节点的单链表,等掌握熟练后自己再尝试不包含头节点的单链表。 

 

  1. // 初始化单链表
  2. ListNode* InitList()
  3. {
  4. // 创建头结点,并进行内存分配
  5. ListNode* head = (ListNode*)malloc(sizeof(ListNode));
  6. // 检查内存分配是否成功
  7. if (head == NULL)
  8. {
  9. perror("malloc fail");
  10. exit(-1);
  11. }
  12. // 将头结点的指针域置空
  13. head->next = NULL;
  14. return head;
  15. }

 2、头插法建立单链表

链表与顺序表不同,它是一种动态管理的存储结构,链表中的每个结点占用的存储空间不是预先分配,而是运行时系统根据需求生成的。因此,建立在初始化链表后,建立线性链表从空表开始,每读入有效的数据则申请一个结点s,并将读取的数据存放到新结点s的数据域中,然后将新结点插入到当前链表头结点head之后,直到循环结束为止。

注意:代码中head为二级指针,即链表头结点的地址。

*head 是一个解引用操作,意味着它获取了 head 指针所指向的值。在这种情况下,head 是一个指向链表头结点指针的指针,通过 *head 可以获取到链表头结点指针的值。

通过 *head 可以访问并修改链表的头指针。在 AddListHead 函数中,(*head)->next 用于获取头结点的下一个节点的指针,而 (*head)->next = newnode 则是将新节点的地址赋值给头结点的下一个节点指针,从而实现在链表头部插入新节点的操作。

  1. void CreateListHead(ListNode** head)
  2. {
  3. assert(head && *head);
  4. int i, n, val;
  5. // 获取要增加的节点数
  6. printf("需要增加多少个节点:");
  7. scanf("%d", &n);
  8. // 循环增加新节点
  9. for (i = 0; i < n; i++)
  10. {
  11. // 获取用户输入的节点值
  12. printf("请输入第 %d 个节点的数据:", i + 1);
  13. scanf("%d", &val);
  14. // 创建新节点,并进行内存分配检查
  15. ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
  16. // 检查内存分配是否成功
  17. if (newnode == NULL)
  18. {
  19. perror("malloc fail");
  20. exit(-1);
  21. }
  22. // 将节点值存储在新节点中
  23. newnode->data = val;
  24. // 将新节点插入到链表头部:将新节点的下一个节点指向当前头节点的下一个节点
  25. // 然后将新节点设为头节点的下一个节点
  26. newnode->next = (*head)->next;
  27. (*head)->next = newnode;
  28. }
  29. }

3、尾插法建立单链表 

 头插法建立链表虽然算法简单易理解,但生成的链表中结点的次序和原输入的次序相反。而尾插法建立链表可实现次序的一致,该算法依旧从空表开始,但需增加一个尾指针last,使其指向当前链表的尾结点。其过程是:每读入有效的数据则申请一个结点s,并将读取的数据存放到新结点s的数据域中,将s的尾指针设为空指针(NULL),然后将新结点插入到当前链表尾部(last 指针所指的结点后面),直到循环结束为止。

  1. void CreateListLast(ListNode** head)
  2. {
  3. assert(head && *head);
  4. int i, n, val;
  5. // 获取要增加的节点数
  6. printf("需要增加多少个节点:");
  7. scanf("%d", &n);
  8. // 创建临时指针并指向链表的头节点
  9. ListNode* temp = *head;
  10. // 移动到链表的最后一个节点
  11. while (temp->next)
  12. {
  13. temp = temp->next;
  14. }
  15. // 循环增加新节点
  16. for (i = 0; i < n; i++)
  17. {
  18. // 获取用户输入的节点值
  19. printf("请输入第 %d 个节点的数据:", i + 1);
  20. scanf("%d", &val);
  21. // 创建新节点,并进行内存分配检查
  22. ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
  23. // 检查内存分配是否成功
  24. if (newnode == NULL)
  25. {
  26. perror("malloc fail");
  27. exit(-1);
  28. }
  29. // 将节点值存储在新节点中
  30. newnode->data = val;
  31. newnode->next = NULL;
  32. // 将新节点连接到链表的最后一个节点,并更新最后一个节点为新节点
  33. temp->next = newnode;
  34. temp = newnode;
  35. }
  36. }

 4、求单链表长度

  1. 检查头节点是否为空,如果为空则无法计算节点数量,可以抛出异常或者进行其他错误处理。
  2. 初始化指针 p,指向链表的第一个节点(头节点的下一个节点)。
  3. 初始化计数器 count 为 0。
  4. 使用循环遍历链表,直到 p 为 NULL(链表遍历结束)。
  5. 在循环中,每遍历到一个节点,计数器 count 加 1。
  6. 将指针 p 指向下一个节点,继续遍历。
  7. 遍历结束后,返回计数器 count 的值,即链表的节点数量。

注意:头节点不计入链表长度。

  1. int LengthList(ListNode* head)
  2. {
  3. // 检查头结点是否为空
  4. assert(head);
  5. // 初始化指针p,指向链表的第一个节点
  6. ListNode* p = head->next;
  7. // 初始化计数器count0
  8. int count = 0;
  9. // 使用循环遍历链表,计算节点数量
  10. while (p != NULL)
  11. {
  12. // 每遍历到一个节点,计数器加1
  13. count++;
  14. // 将指针p指向下一个节点
  15. p = p->next;
  16. }
  17. // 返回链表节点的数量
  18. return count;
  19. }

说明:本函数中仅对单链表进行遍历,未对链表内容进行改变,所以函数形参为一级指针即可,即头指针。


5、按值查找元素

  1. 初始化变量 j 为 1,指针 p 指向头节点的下一个节点。
  2. 使用循环遍历链表,直到找到值等于 x 的节点或者遍历结束。
  3. 在循环中,检查当前节点 p 的 data 值是否等于 x。如果相等,则找到了指定值的节点。打印提示信息,显示在链表的第 j 位上找到值为 x 的节点。
  4. 如果不相等,继续遍历下一个节点。同时,j 增加 1。
  5. 如果遍历结束仍未找到值为 x 的节点,打印提示信息,显示未找到值为 x 的节点。
  1. // 按值查找
  2. void Locate(ListNode* head, DataType x)
  3. {
  4. int j = 1;
  5. ListNode* p;
  6. p = head->next;
  7. while (p != NULL)
  8. {
  9. if (p->data == x)
  10. {
  11. // 在表中找到值为x的结点
  12. printf("在表的第%d位找到值为%d的结点!", j, x);
  13. return;
  14. }
  15. p = p->next;
  16. j++;
  17. }
  18. // 在链表中未找到值为x的结点
  19. printf("未找到值为%d的结点!", x);
  20. }

6、按序号查找元素

  1. 初始化变量 j 为 0,指针 p 指向头节点的下一个节点。
  2. 使用循环遍历链表,直到找到指定位置 i 的节点或者遍历结束。
  3. 在循环中,p 指针向后移动,并且 j 增加 1,直到 j 等于 i 或 p 为 NULL。
  4. 如果 j 等于 i,说明找到了指定位置的节点。打印提示信息,显示在第 i 位上的元素值为 p 的 data 值。
  5. 如果 j 不等于 i,说明链表中没有指定位置的节点。打印提示信息,显示位置错误,链表中没有该位置。
  1. // 按序号查找
  2. void SearchList(ListNode* head, int i)
  3. {
  4. int j = 0;
  5. ListNode* p = head->next;
  6. while (p != NULL && j < i)
  7. {
  8. p = p->next;
  9. j++;
  10. }
  11. if (j == i)
  12. {
  13. // 在第i位上找到结点
  14. printf("在第%d位上的元素值为%d!", i, p->data);
  15. }
  16. else
  17. {
  18. // 位置错误,链表中没有该位置
  19. printf("位置错误,链表中没有该位置!");
  20. }
  21. }

7、在第i个位置前插入节点(元素)

算法思路:

  1. 首先如果链表为空,那么直接新建一个节点,把链表的头指针指向该节点。
  2. 否则,从链表的头节点开始,使用一个指针变量 cur 循环遍历链表找到插入位置的前一个节点。具体操作如下:
    • 初始化指针变量 cur 为链表的头节点。
    • 用计数器 count 从 0 开始往后逐一比较节点,如果当前节点是第 i-1 个节点,则暂停循环。
    • 如果 count 已经是链表中的最后一个节点,那么说明插入位置超出了链表的长度,插入失败。
  3. 如果插入位置符合要求,则在 cur 节点后插入新节点,并更新相应指针。具体操作如下:
    • 新建一个节点 newnode,将元素值赋给它。
    • 将新节点newnode的 next 指针指向 cur->next。
    • 将 cur->next 指针指向 newnode。
  1. // 链表插入节点函数
  2. void InsertNode(ListNode** head, int i, int value)
  3. {
  4. // 创建新节点
  5. ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));
  6. newNode->data = value;
  7. newNode->next = NULL;
  8. // 如果链表为空
  9. if (*head == NULL)
  10. {
  11. newNode->next = *head;
  12. *head = newNode;
  13. printf("插入成功\n");
  14. return;
  15. }
  16. // 找到插入位置的前一个节点
  17. int count = 0;
  18. ListNode* cur = *head;
  19. while (cur->next != NULL && count < i - 1)
  20. {
  21. cur = cur->next;
  22. count++;
  23. }
  24. // 如果插入位置超过链表长度,插入失败
  25. if (cur == NULL)
  26. {
  27. free(newNode);
  28. printf("插入失败\n");
  29. return;
  30. }
  31. // 插入新节点
  32. newNode->next = cur->next;
  33. cur->next = newNode;
  34. printf("插入成功\n");
  35. return;
  36. }

8、在某个值x前插入节点 

  1. 首先,创建一个新的节点 newNode,并给其分配内存空间。将要插入的值 value 赋给新节点 newNode 的 data,将新节点的 next 指针设为 NULL。
  2. 检查链表是否为空,如果为空,则将新节点 newNode 插入链表的头部,并将新节点设为头指针。此时打印提示信息并返回。
  3. 如果链表不为空,需要找到要插入节点的前一个节点。初始化两个指针 prev 和 cur,分别指向 NULL 和头节点 *head。
  4. 使用 prev 和 cur 遍历链表,寻找要插入的值 x。
  5. 如果找到要插入的值 x,将新节点 newNode 插入到 cur 节点之前。即将新节点的 next 指针指向 cur,同时将 prev 的 next 指针指向新节点。同时更新头指针 *head,如果 prev 为 NULL,则表示要插入的节点在链表的头部。
  6. 如果未找到要插入的值 x,说明要插入的节点的值在链表中不存在,插入失败。此时释放新节点所占用的内存空间,并打印提示信息。
  7. 插入成功,打印提示信息。
  1. void InsertNodeBeforeValue(ListNode** head, int x, int value)
  2. {
  3. // 创建新节点
  4. ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));
  5. newNode->data = value;
  6. newNode->next = NULL;
  7. // 如果链表为空
  8. if (*head == NULL)
  9. {
  10. newNode->next = *head;
  11. *head = newNode;
  12. printf("插入成功\n");
  13. return;
  14. }
  15. // 找到要插入节点的前一个节点
  16. ListNode* prev = NULL;
  17. ListNode* cur = *head;
  18. while (cur != NULL)
  19. {
  20. // 如果找到要插入节点的值
  21. if (cur->data == x)
  22. {
  23. // 插入新节点
  24. newNode->next = cur;
  25. if (prev == NULL)
  26. {
  27. *head = newNode;
  28. }
  29. else
  30. {
  31. prev->next = newNode;
  32. }
  33. printf("插入成功\n");
  34. return;
  35. }
  36. prev = cur;
  37. cur = cur->next;
  38. }
  39. // 未找到要插入节点的值,插入失败
  40. free(newNode);
  41. printf("插入失败\n");
  42. }

 9、按位置删除节点

  1. 首先,判断链表是否为空或指定的位置是否无效。如果链表为空或指定的位置小于等于 0,则无法进行删除操作。此时打印提示信息并返回。
  2. 初始化一个指针 cur,指向头节点。
  3. 使用计数器 count 和指针 cur 遍历链表,找到待删除节点的前一个节点。如果计数器 count 小于 index-1,且指针 cur 的下一个节点不为 NULL,则令指针 cur 指向下一个节点,同时计数器 count 自增。
  4. 如果计数器 count 不等于 index-1,或者指针 cur 的下一个节点为 NULL,则表示指定位置不在链表的范围内,删除失败。此时打印提示信息并返回。
  5. 执行到此,说明已经找到待删除节点的前一个节点 cur。使用一个临时指针 temp,指向待删除节点的位置。
  6. 更新 cur 的 next 指针,令其指向待删除节点的下一个节点,从而实现删除操作。释放节点 temp 所占用的内存空间。
  7. 删除成功,打印提示信息。
  1. void DeleteNodeAtPosition(ListNode** head, int index) {
  2. // 链表为空或者索引无效,无法进行删除操作
  3. if (*head == NULL || index <= 0) {
  4. printf("删除失败\n");
  5. return;
  6. }
  7. ListNode* cur = *head;
  8. int count = 0;
  9. // 找到待删除节点的前一个节点
  10. while (count < index - 1 && cur->next != NULL) {
  11. cur = cur->next;
  12. count++;
  13. }
  14. // 如果索引超出链表范围,删除失败
  15. if (count != index - 1 || cur->next == NULL) {
  16. printf("删除失败\n");
  17. return;
  18. }
  19. // 删除节点
  20. ListNode* temp = cur->next;
  21. cur->next = cur->next->next;
  22. free(temp);
  23. printf("删除成功\n");
  24. return;
  25. }

 10、删除单链表中所有数据域等于x的节点

  1. 初始化一个指针 cur,指向链表的头节点。
  2. 遍历链表,如果 cur 的下一个节点的数据域值等于 x,说明该节点需要被删除。此时,创建一个临时节点指针 temp,保存 cur 的下一个节点的地址。修改 cur 的 next 指针,将其指向 temp 的下一个节点。cur仍需在当前位置,不需要移动至下一个位置。释放 temp 指向的节点,完成删除操作。
  3. 如果 cur 的下一个节点的数据域值不等于 x,则将 cur 移动到下一个节点,直到遍历到链表的末尾。
  4. 判断链表删除是否成功,即判断 cur 的 next 指针是否为 NULL。如果为 NULL,则表示链表中不存在值为 x 的节点,删除失败;否则表示链表中至少存在一个值为 x 的节点,删除成功。
  1. void DeleteNodeByValue(ListNode** head, int x)
  2. {
  3. assert(head && *head); // 确保指针不为空
  4. ListNode* cur = *head; // 当前节点指针
  5. while (cur->next != NULL) // 遍历链表
  6. {
  7. if (cur->next->data == x) // 找到要删除的节点
  8. {
  9. ListNode* temp = cur->next; // 临时保存要删除的节点
  10. // 修改指针,将当前节点的下一个节点指向要删除节点的下一个节点
  11. cur->next = cur->next->next;
  12. free(temp); // 释放要删除节点的内存
  13. }
  14. else // 当前节点不是要删除的节点
  15. {
  16. cur = cur->next; // 移动到下一个节点
  17. }
  18. }
  19. if (cur->next == NULL)
  20. {
  21. printf("删除成功\n");
  22. return;
  23. }
  24. printf("删除失败\n");
  25. }

11、输出单链表数据

  1. 首先,判断链表是否为空。检查 head 的 next 指针是否为 NULL,如果为 NULL,则表示链表为空,打印提示信息并返回。
  2. 初始化一个指针 cur,指向链表的第一个节点(即头节点的下一个节点)。
  3. 遍历链表,依次访问每个节点。在循环中,当cur不为空时,打印当前节点的数据域值(假设为整数类型)。
  4. 将 cur 移动到下一个节点,直到遍历完整个链表。
  5. 循环结束后,打印换行符,完成打印操作。
  1. // 打印链表数据
  2. void PrintList(ListNode* head)
  3. {
  4. if (head->next == NULL)
  5. {
  6. printf("链表为空\n");
  7. return;
  8. }
  9. ListNode* cur = head->next;
  10. while (cur != NULL)
  11. {
  12. printf("%d ", cur->data);
  13. cur = cur->next;
  14. }
  15. printf("\n");
  16. }

12、销毁单链表 

  1. 首先,确保链表不为空。检查 head 是否为空指针,如果为空,则断言失败,程序会中止执行。
  2. 初始化一个指针 cur,指向 head。
  3. 遍历链表,依次释放每个节点所占用的内存空间。
  4. 在循环中,先保存当前节点的下一个节点的地址,然后释放当前节点的内存空间(如果颠倒顺序则无法找到下一个节点的地址),最后将当前节点指针 cur 指向下一个节点。
  5. 重复执行步骤 4 直到遍历到链表的末尾。
  6. 销毁链表之后,可以将头指针的值设为 NULL。
  1. void ListDestory(ListNode* head)
  2. {
  3. assert(head);
  4. ListNode* cur = head;
  5. while (cur != NULL)
  6. {
  7. head = cur->next;
  8. free(cur);
  9. cur = head;
  10. }
  11. }

完整测试代码: 

  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #include<assert.h>
  4. typedef int DataType;
  5. typedef struct ListNode
  6. {
  7. DataType data;
  8. struct ListNode* next;
  9. }ListNode;
  10. ListNode* InitList();
  11. void CreateListHead(ListNode** head);
  12. void CreateListLast(ListNode** head);
  13. int LengthList(ListNode* head);
  14. void Locate(ListNode* head, DataType x);
  15. void SearchList(ListNode* head, int i);
  16. void InsertNode(ListNode** head, int i, int value);
  17. void InsertNodeBeforeValue(ListNode** head, int x, int value);
  18. void DeleteNodeByValue(ListNode** head, int x);
  19. void DeleteNodeAtPosition(ListNode** head, int index);
  20. void PrintList(ListNode* head);
  21. void ListDestory(ListNode* head);
  22. //初始化
  23. ListNode* InitList()
  24. {
  25. ListNode* head = (ListNode*)malloc(sizeof(ListNode));
  26. if (head == NULL)
  27. {
  28. perror("malloc fail");
  29. exit(-1);
  30. }
  31. head->next = NULL;
  32. return head;
  33. }
  34. void CreateListHead(ListNode** head)
  35. {
  36. assert(head && *head);
  37. int i, n, val;
  38. // 获取要增加的节点数
  39. printf("需要增加多少个节点:");
  40. scanf("%d", &n);
  41. // 循环增加新节点
  42. for (i = 0; i < n; i++)
  43. {
  44. // 获取用户输入的节点值
  45. printf("请输入第 %d 个节点的数据:", i + 1);
  46. scanf("%d", &val);
  47. // 创建新节点,并进行内存分配检查
  48. ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
  49. // 检查内存分配是否成功
  50. if (newnode == NULL)
  51. {
  52. perror("malloc fail");
  53. exit(-1);
  54. }
  55. // 将节点值存储在新节点中
  56. newnode->data = val;
  57. // 将新节点插入到链表头部:将新节点的下一个节点指向当前头节点的下一个节点
  58. // 然后将新节点设为头节点的下一个节点
  59. newnode->next = (*head)->next;
  60. (*head)->next = newnode;
  61. }
  62. }
  63. void CreateListLast(ListNode** head)
  64. {
  65. assert(head && *head);
  66. int i, n, val;
  67. // 获取要增加的节点数
  68. printf("需要增加多少个节点:");
  69. scanf("%d", &n);
  70. // 创建临时指针并指向链表的头节点
  71. ListNode* temp = *head;
  72. // 移动到链表的最后一个节点
  73. while (temp->next)
  74. {
  75. temp = temp->next;
  76. }
  77. // 循环增加新节点
  78. for (i = 0; i < n; i++)
  79. {
  80. // 获取用户输入的节点值
  81. printf("请输入第 %d 个节点的数据:", i + 1);
  82. scanf("%d", &val);
  83. // 创建新节点,并进行内存分配检查
  84. ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
  85. // 检查内存分配是否成功
  86. if (newnode == NULL)
  87. {
  88. perror("malloc fail");
  89. exit(-1);
  90. }
  91. // 将节点值存储在新节点中
  92. newnode->data = val;
  93. newnode->next = NULL;
  94. // 将新节点连接到链表的最后一个节点,并更新最后一个节点为新节点
  95. temp->next = newnode;
  96. temp = newnode;
  97. }
  98. }
  99. int LengthList(ListNode* head)
  100. {
  101. // 检查头结点是否为空
  102. assert(head);
  103. // 初始化指针p,指向链表的第一个节点
  104. ListNode* p = head->next;
  105. // 初始化计数器count0
  106. int count = 0;
  107. // 使用循环遍历链表,计算节点数量
  108. while (p != NULL)
  109. {
  110. // 每遍历到一个节点,计数器加1
  111. count++;
  112. // 将指针p指向下一个节点
  113. p = p->next;
  114. }
  115. // 返回链表节点的数量
  116. return count;
  117. }
  118. // 按值查找
  119. void Locate(ListNode* head, DataType x)
  120. {
  121. int j = 1;
  122. ListNode* p;
  123. p = head->next;
  124. while (p != NULL)
  125. {
  126. if (p->data == x)
  127. {
  128. // 在表中找到值为x的结点
  129. printf("在表的第%d位找到值为%d的结点!", j, x);
  130. return;
  131. }
  132. p = p->next;
  133. j++;
  134. }
  135. // 在链表中未找到值为x的结点
  136. printf("未找到值为%d的结点!", x);
  137. }
  138. // 按序号查找
  139. void SearchList(ListNode* head, int i)
  140. {
  141. int j = 0;
  142. ListNode* p = head->next;
  143. while (p != NULL && j < i)
  144. {
  145. p = p->next;
  146. j++;
  147. }
  148. if (j == i)
  149. {
  150. // 在第i位上找到结点
  151. printf("在第%d位上的元素值为%d!", i, p->data);
  152. }
  153. else
  154. {
  155. // 位置错误,链表中没有该位置
  156. printf("位置错误,链表中没有该位置!");
  157. }
  158. }
  159. // 链表插入节点函数
  160. void InsertNode(ListNode** head, int i, int value)
  161. {
  162. // 创建新节点
  163. ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));
  164. newNode->data = value;
  165. newNode->next = NULL;
  166. // 如果链表为空或者插入位置是第一个位置
  167. if (*head == NULL)
  168. {
  169. newNode->next = *head;
  170. *head = newNode;
  171. printf("插入成功\n");
  172. return;
  173. }
  174. // 找到插入位置的前一个节点
  175. int count = 0;
  176. ListNode* cur = *head;
  177. while (cur->next != NULL && count < i - 1)
  178. {
  179. cur = cur->next;
  180. count++;
  181. }
  182. // 如果插入位置超过链表长度,插入失败
  183. if (cur == NULL)
  184. {
  185. free(newNode);
  186. printf("插入失败\n");
  187. return;
  188. }
  189. // 插入新节点
  190. newNode->next = cur->next;
  191. cur->next = newNode;
  192. printf("插入成功\n");
  193. return;
  194. }
  195. // 打印链表数据
  196. void PrintList(ListNode* head)
  197. {
  198. if (head->next == NULL)
  199. {
  200. printf("链表为空\n");
  201. return;
  202. }
  203. ListNode* cur = head->next;
  204. while (cur != NULL)
  205. {
  206. printf("%d ", cur->data);
  207. cur = cur->next;
  208. }
  209. printf("\n");
  210. }
  211. void InsertNodeBeforeValue(ListNode** head, int x, int value)
  212. {
  213. // 创建新节点
  214. ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));
  215. newNode->data = value;
  216. newNode->next = NULL;
  217. // 如果链表为空
  218. if (*head == NULL)
  219. {
  220. newNode->next = *head;
  221. *head = newNode;
  222. printf("插入成功\n");
  223. return;
  224. }
  225. // 找到要插入节点的前一个节点
  226. ListNode* prev = NULL;
  227. ListNode* cur = *head;
  228. while (cur != NULL)
  229. {
  230. // 如果找到要插入节点的值
  231. if (cur->data == x)
  232. {
  233. // 插入新节点
  234. newNode->next = cur;
  235. if (prev == NULL)
  236. {
  237. *head = newNode;
  238. }
  239. else
  240. {
  241. prev->next = newNode;
  242. }
  243. printf("插入成功\n");
  244. return;
  245. }
  246. prev = cur;
  247. cur = cur->next;
  248. }
  249. // 未找到要插入节点的值,插入失败
  250. free(newNode);
  251. printf("插入失败\n");
  252. }
  253. void DeleteNodeAtPosition(ListNode** head, int index) {
  254. // 链表为空或者索引无效,无法进行删除操作
  255. if (*head == NULL || index <= 0) {
  256. printf("删除失败\n");
  257. return;
  258. }
  259. ListNode* cur = *head;
  260. int count = 0;
  261. // 找到待删除节点的前一个节点
  262. while (count < index - 1 && cur->next != NULL) {
  263. cur = cur->next;
  264. count++;
  265. }
  266. // 如果索引超出链表范围,删除失败
  267. if (count != index - 1 || cur->next == NULL) {
  268. printf("删除失败\n");
  269. return;
  270. }
  271. // 删除节点
  272. ListNode* temp = cur->next;
  273. cur->next = cur->next->next;
  274. free(temp);
  275. printf("删除成功\n");
  276. return;
  277. }
  278. void DeleteNodeByValue(ListNode** head, int x)
  279. {
  280. assert(head && *head); // 确保指针不为空
  281. ListNode* cur = *head; // 当前节点指针
  282. while (cur->next != NULL) // 遍历链表
  283. {
  284. if (cur->next->data == x) // 找到要删除的节点
  285. {
  286. ListNode* temp = cur->next; // 临时保存要删除的节点
  287. // 修改指针,将当前节点的下一个节点指向要删除节点的下一个节点
  288. cur->next = cur->next->next;
  289. free(temp); // 释放要删除节点的内存
  290. }
  291. else // 当前节点不是要删除的节点
  292. {
  293. cur = cur->next; // 移动到下一个节点
  294. }
  295. }
  296. if (cur->next == NULL)
  297. {
  298. printf("删除成功\n");
  299. return;
  300. }
  301. printf("删除失败\n");
  302. }
  303. void ListDestory(ListNode* head)
  304. {
  305. assert(head);
  306. ListNode* cur = head;
  307. while (cur != NULL)
  308. {
  309. head = cur->next;
  310. free(cur);
  311. cur = head;
  312. }
  313. }
  314. int main()
  315. {
  316. ListNode* head = InitList();
  317. //CreateListLast(&head);
  318. CreateListHead(&head);
  319. PrintList(head);
  320. InsertNode(&head, 2, 11);
  321. PrintList(head);
  322. DeleteNodeAtPosition(&head, 3);
  323. PrintList(head);
  324. DeleteNodeByValue(&head, 3);
  325. PrintList(head);
  326. return 0;
  327. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/636576
推荐阅读
相关标签
  

闽ICP备14008679号