C++链表02:反转链表

1.背景

大家好,我是酒馆店小二。

力扣206.翻转链表。
题意:反转一个单链表。
示例:
输入: 2->3->4->5->NULL
输出: 5->4->3->2->NULL
C++链表02:反转链表_第1张图片

2.迭代

C++链表02:反转链表_第2张图片

如图,

  • 定义pre指针,初始化为 nullptr
  • 定义cur指针,指向头结点;
  • 定义temp指针,指向cur->next节点,为什么要指向这个节点?因为接下来要改变cur->next的指向,将cur->next指向pre;
  • 重复执行上述操作,不断后移precur指针,直到cur指向nullptr,循环结束,链表反转完毕,返回pre指针即可。
    C++链表02:反转链表_第3张图片

说一千道一万,没有代码都不算。

迭代代码:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
ListNode* reverseList(ListNode* head) {
    ListNode* pre = nullptr;  // 定义 pre 指针,初始化为 nullptr;
    ListNode* cur = head;  // 定义 cur 指针,指向头结点;
    while(cur) {
        ListNode* tmp = cur->next;  // 定义 temp 指针,指向 cur->next 节点
        cur->next = pre;
        pre = cur;  // 后移 pre 和 cur 指针
        cur = tmp;
    }
    return pre;
}

3.递归

递归版本稍微复杂一些,其关键在于反向工作。假设链表的其余部分已经被反转,现在应该如何反转它前面的部分?

假设链表为:
n1→…→n k−1→n k→n k+1 →…→n m →∅
若从节点 n k+1 到 n m已经被反转,而我们正处于 n k 。
n1 →…→n k−1 →n k →n k+1 ←…←n m
我们希望 n k+1的下一个节点指向 n k。
所以,nk→next→next = nk。
需要注意的是 n 1的下一个节点必须指向 ∅。如果忽略了这一点,链表中可能会产生环。

ListNode* reverseList(ListNode* head) {
    if (!head || !head->next) return head;
    // 递归调用,翻转第二个节点开始往后的链表
    ListNode* newHead = reverseList(head->next);
    // 反转头节点与第二个节点的指向
    head->next->next = head;
    // head节点指针指为空,因为它已经被它的下一个节点指向了,理论上它就是尾结点
    // 因为通过递归操作找到的就是尾结点,并且反转尾结点
    head->next = nullptr;
    return newHead;
}

4.进阶:反转链表内指定区间

将一个节点数为 size 链表 m 位置到 n 位置之间的区间反转,要求时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( 1 ) O(1) O(1)
例如:
给出的链表为 1→2→3→4→5→NULL,m = 2,n = 4,
返回 1→4→3→2→5→NULL。

解题思路:

  1. 判断参数 head 是否为空,若空则直接返回;判断m是否等于 n,相等则无需进行操作,直接返回;
  2. 创建一个头结点 dummyHead ,以便对整个链表的操作实现统一;
  3. 指针begin初始化为头节点 dummyHead,begin 将作为待反转链表第一个结点的前驱指针。然后通过变量 i (初始化为 0 )记录当前遍历的结点个数,顺序遍历链表的前 m -1个结点时,begin 不断向前移动,最终指向第 m - 1 个结点;
  4. 指针 start 指向待反转链表的第一个结点,初始化为 begin->next ;指针 finish 指向待反转链表的最后一个结点,初始化为 begin。然后在顺序遍历到第 n 个结点的过程中,finish不断向前移动,直到指向第 n 个结点。
  5. 指针 end 指向待反转链表的最后一个结点的下一个位置;

反转链表:

  • 初始时,指针 pre 指向 start,指针 cur 指向 start->next,begin->next = nullptr,finish->next = nullptr;
  • 反转过程: tmp = cur->next; cur->next = pre;pre = cur;cur = tmp
  • 结束条件:cur == finish
  • 最后一步:将反转后的链表与其余 begin 之前的链表和 end 之后的链表进行连接:
    begin->next = finish; start->next = end;

C++链表02:反转链表_第4张图片
C++链表02:反转链表_第5张图片

C++链表02:反转链表_第6张图片

ListNode *reverseBetween(ListNode *head, int m, int n) {
    if (!head || m == n) return head;
    
    ListNode *dummyHead = new ListNode(-1);
    dummyHead->next = head;  // 创建链表的头结点
    ListNode *begin = dummyHead;  // begin 初始化为虚拟头结点
    
    int i = 0;
    while (i < m - 1) {
        begin = begin->next;  // 循环结束,begin 指向第 m - 1个节点,即待反转链表前一个节点
        ++i;
    }
    
	ListNode *start = begin->next;
    ListNode *finish = begin;
    while (i < n) {
        finish = finsh->next;   // 循环结束,finish 指向第 n 个节点,即待反转链表最后一个节点
        ++i;
    }
    ListNode *end = finish->next;  // end 指向待反转链表最后一个节点之后的一个节点
    
    ListNode *pre = start;
    ListNode *cur = start->next;
    begin->next = nullptr;
    finish->next = nullptr;
    
    while (pre != finish) {
        ListNode *tmp = cur->next;
        cur->next = pre;
        pre = cur;
        cur = tmp;
    }
	begin->next = finish;
    start->next = end;
    head = dummyHead->next;
    delete dummyHead;
    return head;
}

当时轻别意中人,山长水远知何处。
2022.3.23

你可能感兴趣的:(algorithm,c++,c++,算法,链表)