前言
链表翻转是很典型的场景案例了。在面试中被问过,挺有趣的,记录下。
链表翻转
给定一个链表,将其翻转,如下
理解起来是很简单的。
下面给定链表节点的结构
struct ListNode {
int val;
struct ListNode *next;
};
非递归
对于翻转这个动作,例 A->B->NULL ,可以当A的父节点也是NULL。则:
(1)当前节点为A,前一个为NULL,A->next 指向NULL
(2)将当前节点变为B,前一个变为 A,B->next 指向A
(3)…
所以我们只需要cur和pre两个指针,然后改变cur的next指针指向,这里记得保存原来cur的next指针。
第一次调整后
注意,Cur和Pre之间是已经没有指针指向了的,判断到Cur为NULL就停下来,则接下来是最后一次翻转
class Solution {
public:
ListNode* ReverseList(ListNode* pHead) {
ListNode* current = pHead;
ListNode* pre = NULL;
ListNode* next = NULL;
while(current != NULL){
next = current->next;
current->next = pre;
pre = current;
if(next == NULL) break;
current = next;
}
pHead = current;
return pHead;
}
};
递归
翻转链表的递归做法可以说是递归届的经典了(误),递归写法先品尝代码会更有味道。
经典祖传代码如下
class Solution {
public:
ListNode* ReverseList(ListNode* pHead) {
if(!pHead) return pHead;
if (pHead->next == NULL) return pHead;
ListNode *last = ReverseList(pHead->next);//找到最后一个
pHead->next->next = pHead;//翻转
pHead->next = NULL;
return last;//返回的是最新的头结点
}
};
名言:递归时一定不要陷进去,脑子里能压几个栈啊?
在这个递归问题中,我们要想两个事情
1、子问题是什么?
2、最底层要返回什么?
首先回答第一个
子问题:链表翻转,对一个节点,使它的下一个节点的next指针指向自己,自己的next指向NULL悬挂,就达到了翻转目的。
例如 A->B->NULL 变成 NULL<-A<-B 。
所以能看到代码中处理子问题的部分为:
pHead->next->next = pHead;//翻转
pHead->next = NULL;
第二个问题,要返回什么?
反转链表最后返回的肯定是最新的头结点啦!所以一路将 last 节点护送了回来。
ListNode *last = ReverseList(pHead->next);//找到最后一个
return last;
链表每k个节点翻转
这个情景是上面的一个变化,其实关键就是注意好每次处理完翻转的返回值是新的翻转的子链表的头结点,这里只写非递归形式。
非递归
ListNode* reverse(ListNode *first, ListNode *last){
ListNode *pre = nullptr;
while(first != last){
ListNode *temp = first->next;
first->next = pre;
pre = first;
first = temp;
}
return pre;
}
ListNode* reverseKGroup(ListNode* head, int k) {
// write code here
if(!head) return nullptr;
ListNode *node = head;
for(int i=0;i<k;i++){
if(!node) return head;
node = node->next;
}
ListNode *newHead = reverse(head,node);
head->next = reverseKGroup(node,k);
return newHead;
}