这道题乍一看,这有啥难度 就是把反转链表变成两个两个反转
思路没有错的 解决的问题分别是:
1 如何反转链表
2 如何按照指定长度反转链表
我们一个一个来看,首先是如何反转链表,绝大多数人首先想到的一定是使用一个前置指针prev进行反转。例如下面所示:
ListNode * reverseListNode(ListNode * head){
if(head == nullptr) return nullptr;
if(head->next == nullptr) return head;
ListNode * prevNode = nullptr;
ListNode * currentNode = head;
while(currentNode!=nullptr){
ListNode * tmpNode = currentNode->next;
currentNode->next = prevNode;
prevNode = currentNode;
currentNode = tmpNode;
}
return prevNode;
}
之后是何如K个一组反转链表呢?同leetcode25题。
首先也需要有一个反转链表的函数,我们对上面的函数做一个小小改动添加一个lastNode的参数
//反转位置是head到last-1的链表
ListNode * reverseListNode(ListNode * head, ListNode * last){
if(head == nullptr) return nullptr;
if(head->next == nullptr) return head;
ListNode * currentNode = head;
ListNode * prevNode = nullptr;
while(currentNode != last){
auto tmpNode = currentNode->next;
currentNode->next = prevNode;
prevNode = currentNode;
currentNode = tmpNode;
}
return prevNode;
}
ListNode * reverseGroup(ListNode * head, int k){
ListNode * last = head;
//注意这里实际上last已经移动到了[k]
for(auto i = 0; i < k; i++){
if(last != nullptr){
last = last->next;
}else{
return head;
}
}
auto lastNode = reverse(head, last);
head->next = reverseGroup(last, k);
return lastNode;
}
其实这里还有一个问题,如果链表中最后一组不满k个数,可以分为两种输出结果,一种是继续反转剩余不满k个的那组,一个是剩余不满的那组不反转。这里题目中是不反转,如果改为需要反转,可以直接修改为:
for(auto i = 0; i < k; i++){
if(last != nullptr){
last = last->next;
}else{
return head;
}
}
=======================================
for(auto i = 0; i < k; i++){
if(last != nullptr){
last = last->next;
}else{
//注意修改这里
return reverse(head,last);
}
}
这是面试官最喜欢问的一个变种。
回到最初的问题,两个两个交换是不是就是直接把k设置为2.
当然这里有一个更简单的解法,利用递归的方式:
ListNode* swapPairs(ListNode* head) {
if(head == nullptr) return nullptr;
if(head->next == nullptr) return head;
ListNode* newhead = head->next;
//这里递归
head->next = swapPairs(newhead->next);
newhead->next = head;
return newhead;
}
这种递归的方式是不是会有栈溢出的风险呢?
2021.01.26晚更新
今天在公司和同事讨论了这个问题,在此感谢zhanglei541120这个小哥哥提出了可以使用尾递归解决这个栈溢出的问题。
首先理解一下尾递归这里我直接放上百度百科:
现代编译器大都都能做到这样的优化,于是我们稍微修改上述代码:
void swapPairsInternal(ListNode * prev, ListNode * start){
if(start == nullptr || start -> next == nullptr) {prev->next = start; return;};
auto tmp = start->next->next;
prev -> next = start->next;
start-> next -> next = start;
//这里实现了尾递归
swapPairsInternal(start, tmp);
}
ListNode* swapPairs(ListNode* head) {
if(head == nullptr || head -> next == nullptr){
return head;
}
auto newhead = head -> next;
//简单的方法就是用传参的方式替换栈中的局部变量
swapPairsInternal(head, head->next->next);
newhead->next = head;
return newhead;
}
经过验证:g++ test.cc -O2及其以上的优化可以实现尾递归。在实验中我构造一个循环链表,执行程序过程中不停pstack,始终只有一层栈main,甚至连swapPairs这层栈也被inline内联。