简单
剑指 Offer 24. 反转链表
反转链表操作作为链表操作的最基本操作,一般可以通过迭代或者递归的方式解决,这里以反转链表为例,讨论反转链表相关问题。
反转全部链表
反转全部链表,即1->2->3->4->5->NULL
转变成NULL<-1<-2<-3<-4<-5
,反转全部链表是比较简单的操作,先来看一下递归的解决代码:
ListNode* reverseList(ListNode* head) {
if (head == NULL) return NULL; // 传入的链表可能为空的情况
if (head->next == NULL) return head;
ListNode* last = reverseList(head->next);
head->next->next = head;
head->next = NULL;
return last;
}
对于递归解决的理解,首先不要跳进递归的调用中,这样很容易把自己绕晕了,考虑最原始的思路,递归函数是干嘛的?这里reverseList
函数,接受一个头指针head
指向头节点,然后反转head
与head->next
两个节点的顺序,然后递归处理head->next
后面的节点,注意由于最后需要返回的是反转后链表的头节点,因此这里需要返回的是last
。同时还要注意,递归函数是需要递归出口的,当只有一个节点时,直接返回即可,不用再反转了。
反转链表的前N个节点
与反转整个链表类似,反转前N个节点,需要在反转节点时,计算反转次数,反转N次则代表已经结束。
ListNode* after = NULL; // 后继节点
ListNode* reverseListN(ListNode* head, int n) {
if (n == 1) {
after = head->next; // 记录断开的节点
return head;
}
ListNode* last = reverseListN(head->next, n - 1);
head->next->next = head;
head->next = after; // 连接
return last;
}
这里默认n不会超过链表长度,因此递归出口只需判断n是否为1,为1时表示处理结束。另外,我们发现用到了后继节点,因为在反转前N个节点时,被反转的部分的尾结点是指向空的,因此需要将其指向断开的节点。
中等
92. 反转链表 II
反转链表中的[left, right]中的节点
left,right
分别表示第left,right
个节点,可以发现,这个问题是在前面的基础上再次加深了,但不要被其所迷惑,我们已经解决了反转链表的前n个节点,如果我们找到left
节点,这时,将left
节点作为头节点,此时就是反转前n个节点了。
ListNode* reverseListBetween(ListNode* head, int left, int right) {
if (left == 1) {
return reverseListN(head, right);
}
head->next = reverseListBetween(head->next, left - 1, right - 1);
return head;
}
这里需要注意的是,最终递归结束后,reverseListN
返回的是right
所在的节点,因此在递归时,需要将前后连接,即head->next = reverseListBetween(head->next, left - 1, right - 1)
需要注意的是,以上三种解决办法都是通过递归实现的,但由于递归需要调用栈,因此它的空间复杂度是
O(n)
,而迭代实现的空间复杂度是O(1)
困难
K 个一组翻转链表
对于这道题,我们先考虑如何反转整个链表,这个操作应该不难
ListNode* reverse(ListNode* a, ListNode* b) {
ListNode* pre, *cur, *nxt;
pre = nullptr, cur = a, nxt = a;
while(cur != nullptr) {
nxt = cur->next;
cur->next = pre;
pre = cur;
cur = nxt;
}
return pre;
}
那么,在此基础上,如何反转节点[a,b)
之间的链表呢?其实只需要在上面代码中修改cur != b
即可。
如果我们将k个一组反转,拆解成若干个子问题,会发现这些子问题都是类似的,即以某个节点为头节点,链表长度为k,反转整个链表。
ListNode* reverseKGroup(ListNode* head, int k) {
ListNode* b = head;
for (int i = 0; i < k; i++) {
if (b == nullptr) return head; // 若不足k个,保持原有顺序
b = b->next;
}
ListNode* pre = reverse(head, b); // 记录反转后的头节点
head->next = reverseKGroup(b, k); // 将前面子问题反转后的链表与后面反转后的链表连接
return pre;
}