1.背景
大家好,我是酒馆店小二。
力扣206.翻转链表。
题意:反转一个单链表。
示例:
输入: 2->3->4->5->NULL
输出: 5->4->3->2->NULL
2.迭代
如图,
- 定义
pre
指针,初始化为nullptr
; - 定义
cur
指针,指向头结点; - 定义
temp
指针,指向cur->next
节点,为什么要指向这个节点?因为接下来要改变cur->next
的指向,将cur->next
指向pre
; - 重复执行上述操作,不断后移
pre
和cur
指针,直到cur
指向nullptr
,循环结束,链表反转完毕,返回pre
指针即可。
说一千道一万,没有代码都不算。
迭代代码:
/**
* 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。
解题思路:
- 判断参数 head 是否为空,若空则直接返回;判断m是否等于 n,相等则无需进行操作,直接返回;
- 创建一个头结点 dummyHead ,以便对整个链表的操作实现统一;
- 指针begin初始化为头节点 dummyHead,begin 将作为待反转链表第一个结点的前驱指针。然后通过变量 i (初始化为 0 )记录当前遍历的结点个数,顺序遍历链表的前 m -1个结点时,begin 不断向前移动,最终指向第 m - 1 个结点;
- 指针 start 指向待反转链表的第一个结点,初始化为 begin->next ;指针 finish 指向待反转链表的最后一个结点,初始化为 begin。然后在顺序遍历到第 n 个结点的过程中,finish不断向前移动,直到指向第 n 个结点。
- 指针 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;
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