• 反转链表和哨兵位


    目录

    法一

    法2

    法3(哨兵位头节点)

    // 创建新节点

    // 链表逆序

    // ... 其他链表操作函数(如打印链表等)

    使用哨兵位头节点的链表逆序

    简易的两种方法


    在编程中,尤其是在处理数组或数据流时,“哨兵位”可以是一种特殊的值,用来标记某些条件或异常情况。例如,你可以在数组的第一个元素放置一个特殊的值,用来表示数组的结束或者某种特定的状态。

    以下是一个简单的例子,展示了如何在C语言中使用“哨兵位”:

    1. #include <stdio.h>
    2. int main() {
    3. int array[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, -1}; // 假设-1用作哨兵值
    4. int n = sizeof(array) / sizeof(array[0]);
    5. int i;
    6. for (i = 0; array[i] != -1; i++) {
    7. // 处理数组中的每个元素,直到遇到哨兵值
    8. printf("%d ", array[i]);
    9. }
    10. printf("\n");
    11. return 0;
    12. }

    在这个例子中,数组的第一个元素被设置为-1,用作哨兵值。在循环中,我们检查当前元素是否等于哨兵值。如果等于,循环结束;否则,我们处理该元素。

    这种方法常用于处理不确定长度的数据流,或者在数据处理过程中需要一个明确的结束标志的情况。通过使用哨兵位,你可以简化代码逻辑,避免使用复杂的状态变量或额外的数据结构。

    法一

    1. /**
    2. * Definition for singly-linked list.
    3. * struct ListNode {
    4. * int val;
    5. * struct ListNode *next;
    6. * };
    7. */
    8. struct ListNode* reverseList(struct ListNode* head)
    9. {
    10. //法1
    11. // if(head==NULL)
    12. // {
    13. // return NULL;
    14. // }
    15. // struct ListNode*n1=NULL,*n2=head,*n3=NULL;
    16. // n1=NULL;
    17. // n2=head;
    18. // n3=n2->next;
    19. // while(n2)
    20. // {
    21. // n2->next=n1;
    22. // n1=n2;
    23. // n2=n3;
    24. // if(n3!=NULL)
    25. // {
    26. // n3=n3->next;
    27. // }
    28. // }
    29. // return n1;

    法2

    1. //法二,画图
    2. struct ListNode*cur=NULL;
    3. struct ListNode*tail=head;
    4. while(tail)
    5. {
    6. struct ListNode*p=tail->next;
    7. tail->next=cur;
    8. cur=tail;
    9. tail=p;
    10. }
    11. return cur;

    法3(哨兵位头节点)

    在C语言中,链表逆序同样可以不使用哨兵位头节点,但使用哨兵位头节点可以简化某些边界情况的处理。以下我将分别展示两种方法的示例代码。不使用哨兵位头节点的链表逆序
     

    1. #include
    2. #include
    3. // 定义链表节点结构体
    4. typedef struct ListNode {
    5.     int val;
    6.     struct ListNode *next;
    7. } ListNode;

    // 创建新节点

    1. ListNode* createNode(int val) {
    2.     ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));
    3.     if (!newNode) {
    4.         return NULL;
    5.     }
    6.     newNode->val = val;
    7.     newNode->next = NULL;
    8.     return newNode;
    9. }

    // 链表逆序

    1. ListNode* reverseList(ListNode* head) {
    2.     ListNode *prev = NULL;
    3.     ListNode *curr = head;
    4.     ListNode *next = NULL;
    5.     
    6.     while (curr != NULL) {
    7.         next = curr->next;  // 保存下一个节点
    8.         curr->next = prev;  // 将当前节点的next指向前一个节点
    9.         prev = curr;        // 移动prev到当前节点
    10.         curr = next;        // 移动curr到下一个节点
    11.     }
    12.     
    13.     // prev现在指向新的头节点
    14.     return prev;
    15. }

    // ... 其他链表操作函数(如打印链表等)

    1. int main() {
    2.     // 构造链表:1 -> 2 -> 3 -> 4 -> 5
    3.     ListNode *head = createNode(1);
    4.     head->next = createNode(2);
    5.     head->next->next = createNode(3);
    6.     head->next->next->next = createNode(4);
    7.     head->next->next->next->next = createNode(5);
    8.     // 逆序链表
    9.     head = reverseList(head);
    10.     // ... 打印链表等操作
    11.     return 0;
    12. }

    使用哨兵位头节点的链表逆序

    使用哨兵位头节点时,我们需要在链表的最前面添加一个额外的节点,并将它的next指针指向原始链表的头节点。逆序完成后,我们可以直接返回哨兵位头节点的next指针作为新链表的头节点。

    在这个代码中,我们首先定义了链表节点的结构体ListNode,然后提供了创建新节点的函数createNodereverseList函数用来逆序链表,它使用了一个哨兵头节点,即第一个节点作为prev指针的初始位置,最后将头节点更新为prev指针所指向的节点。printList函数用来打印链表的节点值。

    main函数中,我们创建了一个简单的链表,并调用reverseList函数进行逆序,然后打印出逆序后的结果。最后,我们释放了链表占用的内存。

    请注意,在实际的项目开发中,还需要对代码进行适当的错误处理和边界条件检查,以确保程序的健壮性。

    在C语言中,使用哨兵位头节点(dummy head)来逆序链表是一种常见的技巧,因为它可以简化边界条件的处理。以下是一个使用哨兵位头节点逆序单链表的示例代码:

    // 链表节点的结构体定义

    1. typedef struct ListNode {
    2. int val;
    3. struct ListNode *next;
    4. } ListNode;

    // 创建一个新的链表节点

    1. ListNode* createNode(int value) {
    2. ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));
    3. if (!newNode) {
    4. exit(-1); // 分配内存失败,退出程序
    5. }
    6. newNode->val = value;
    7. newNode->next = NULL;
    8. return newNode;
    9. }

    // 在链表的末尾添加一个新节点

    1. void appendNode(ListNode** headRef, int value) {
    2. ListNode* newNode = createNode(value);
    3. ListNode* temp = *headRef;
    4. if (temp == NULL) {
    5. *headRef = newNode;
    6. return;
    7. }
    8. while (temp->next != NULL) {
    9. temp = temp->next;
    10. }
    11. temp->next = newNode;
    12. }

    // 逆序链表,使用哨兵位头节点

    1. void reverseList(ListNode** headRef) {
    2. ListNode* prev = NULL;
    3. ListNode* current = *headRef;
    4. ListNode* next = NULL;
    5. while (current != NULL) {
    6. next = current->next; // 保存下一个节点
    7. current->next = prev; // 当前节点指向前一个节点
    8. prev = current; // 前一个节点移动到当前节点
    9. current = next; // 当前节点移动到下一个节点
    10. }
    11. *headRef = prev; // 更新头节点
    12. }

    // 打印链表

    1. void printList(ListNode* head) {
    2. ListNode* temp = head;
    3. while (temp != NULL) {
    4. printf("%d ", temp->val);
    5. temp = temp->next;
    6. }
    7. printf("\n");
    8. }

    // 主函数,测试逆序链表功能

    1. int main() {
    2. ListNode* head = NULL; // 哨兵位头节点
    3. // 向链表中添加元素
    4. appendNode(&head, 1);
    5. appendNode(&head, 2);
    6. appendNode(&head, 3);
    7. appendNode(&head, 4);
    8. printf("原始链表:");
    9. printList(head);
    10. // 逆序链表
    11. reverseList(&head);
    12. printf("逆序后链表:");
    13. printList(head);
    14. // 释放链表内存
    15. ListNode* temp;
    16. while (head != NULL) {
    17. temp = head;
    18. head = head->next;
    19. free(temp);
    20. }
    21. return 0;
    22. }

    总代码

    1. #include
    2. #include
    3. // 链表节点的结构体定义
    4. typedef struct ListNode {
    5. int val;
    6. struct ListNode *next;
    7. } ListNode;
    8. // 创建一个新的链表节点
    9. ListNode* createNode(int value) {
    10. ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));
    11. if (!newNode) {
    12. exit(-1); // 分配内存失败,退出程序
    13. }
    14. newNode->val = value;
    15. newNode->next = NULL;
    16. return newNode;
    17. }
    18. // 在链表的末尾添加一个新节点
    19. void appendNode(ListNode** headRef, int value) {
    20. ListNode* newNode = createNode(value);
    21. ListNode* temp = *headRef;
    22. if (temp == NULL) {
    23. *headRef = newNode;
    24. return;
    25. }
    26. while (temp->next != NULL) {
    27. temp = temp->next;
    28. }
    29. temp->next = newNode;
    30. }
    31. // 逆序链表,使用哨兵位头节点
    32. void reverseList(ListNode** headRef) {
    33. ListNode* prev = NULL;
    34. ListNode* current = *headRef;
    35. ListNode* next = NULL;
    36. while (current != NULL) {
    37. next = current->next; // 保存下一个节点
    38. current->next = prev; // 当前节点指向前一个节点
    39. prev = current; // 前一个节点移动到当前节点
    40. current = next; // 当前节点移动到下一个节点
    41. }
    42. *headRef = prev; // 更新头节点
    43. }
    44. // 打印链表
    45. void printList(ListNode* head) {
    46. ListNode* temp = head;
    47. while (temp != NULL) {
    48. printf("%d ", temp->val);
    49. temp = temp->next;
    50. }
    51. printf("\n");
    52. }
    53. // 主函数,测试逆序链表功能
    54. int main() {
    55. ListNode* head = NULL; // 哨兵位头节点
    56. // 向链表中添加元素
    57. appendNode(&head, 1);
    58. appendNode(&head, 2);
    59. appendNode(&head, 3);
    60. appendNode(&head, 4);
    61. printf("原始链表:");
    62. printList(head);
    63. // 逆序链表
    64. reverseList(&head);
    65. printf("逆序后链表:");
    66. printList(head);
    67. // 释放链表内存
    68. ListNode* temp;
    69. while (head != NULL) {
    70. temp = head;
    71. head = head->next;
    72. free(temp);
    73. }
    74. return 0;
    75. }

    在这个代码中,我们首先定义了链表节点的结构体ListNode。然后,我们提供了创建新节点和向链表末尾添加新节点的函数createNodeappendNodereverseList函数用来逆序链表,它使用了一个哨兵头节点prev,并最终将头节点更新为prev指针所指向的节点。printList函数用来打印链表的节点值。

    main函数中,我们创建了一个链表,并调用reverseList函数进行逆序,然后打印出逆序后的结果。最后,我们释放了链表占用的内存。

    请注意,在实际的项目开发中,还需要对代码进行适当的错误处理和边界条件检查,以确保程序的健壮性。

    1. #include
    2. #include
    3. // 定义链表节点结构体
    4. typedef struct ListNode {
    5. int val;
    6. struct ListNode* next;
    7. } ListNode;
    8. // 创建新节点
    9. ListNode* createNode(int value) {
    10. ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));
    11. if (!newNode) {
    12. exit(-1); // 分配内存失败,退出程序
    13. }
    14. newNode->val = value;
    15. newNode->next = NULL;
    16. return newNode;
    17. }
    18. // 逆序链表,使用哨兵头节点
    19. void reverseList(ListNode** headRef) {
    20. ListNode* prev = NULL;
    21. ListNode* current = *headRef;
    22. ListNode* next = NULL;
    23. while (current != NULL) {
    24. next = current->next; // 保存下一个节点
    25. current->next = prev; // 当前节点指向前一个节点,实现逆序
    26. prev = current; // 前一个节点移动到当前节点
    27. current = next; // 当前节点移动到下一个节点
    28. }
    29. *headRef = prev; // 更新头节点
    30. }
    31. // 打印链表
    32. void printList(ListNode* head) {
    33. ListNode* temp = head;
    34. while (temp != NULL) {
    35. printf("%d ", temp->val);
    36. temp = temp->next;
    37. }
    38. printf("\n");
    39. }
    40. // 主函数,测试逆序链表功能
    41. int main() {
    42. ListNode* head = createNode(1);
    43. head->next = createNode(2);
    44. head->next->next = createNode(3);
    45. head->next->next->next = createNode(4);
    46. printf("原始链表:");
    47. printList(head);
    48. reverseList(&head);
    49. printf("逆序后链表:");
    50. printList(head);
    51. // 释放链表内存
    52. ListNode* temp;
    53. while (head != NULL) {
    54. temp = head;
    55. head = head->next;
    56. free(temp);
    57. }
    58. return 0;
    59. }


    注意,在使用哨兵位头节点的示例中,我们不需要特别处理头节点为空的情况,因为哨兵位头节点始终存在。此外,在逆序过程中,我们还需要注意处理原链表的尾节点,因为当curr指向尾节点时,curr->next将为NULL,我们不需要再将其指向前一个节点。

    简易的两种方法

    1. // 方法1:使用三个指针
    2. ListNode* reverseList(ListNode* head) {
    3. ListNode *prev = NULL;
    4. ListNode *current = head;
    5. ListNode *next_node = NULL;
    6. while (current != NULL) {
    7. next_node = current->next;
    8. current->next = prev;
    9. prev = current;
    10. current = next_node;
    11. }
    12. return prev;
    13. }
    14. // 方法2:使用哨兵位头节点
    15. ListNode* reverseList2(ListNode* head) {
    16. ListNode dummy;
    17. dummy.next = head;
    18. ListNode *prev = &dummy;
    19. ListNode *current = head;
    20. while (current != NULL) {
    21. ListNode *next_node = current->next;
    22. current->next = prev->next;
    23. prev->next = current;
    24. current = next_node;
    25. }
    26. return dummy.next;
    27. }

  • 相关阅读:
    一个使用AndroidStudio实现的简单逆波兰表达式计算求值的App,算是安卓App入门练手项目吧
    云原生编程挑战赛落幕,阿里云推出云原生领域首本《应用多活技术白皮书》
    字节研发之道
    商城免费搭建之java商城 开源java电子商务Spring Cloud+Spring Boot+mybatis+MQ+VR全景+b2b2c
    漏洞修复:Content-Security-Policy header missing
    matplotlib数据可视化
    【王道计算机组成原理】第三章 存储系统
    【PyTorch】(四)----完整训练流程
    容错限流框架之Hystrix上
    Java8新特性之Stream流(含具体案例)
  • 原文地址:https://blog.csdn.net/2401_83427936/article/details/139363227