反转链表的进阶版:LeetCode第 206 题:反转链表(C++)_qq_32523711的博客-CSDN博客
链表操作的关键点在于现场保护,本题反转区间之前、之后的节点需要在反转之后连接到正确的位置。翻转是简单的部分,难点在于现场保护和恢复。
可以迭代求解也可以递归求解:
- 迭代的话,一次遍历就可以获取节点总个数n,n/k即为需要翻转的次数,每次翻转k个节点,需要让k-1个指针反向
- 递归的话,同样需要操作n/k次,定义函数
reverse(node, k)
将节点node
之后(不包括node
)的k个节点进行翻转(难点还是现场的维护),返回翻转后的链表头结点(建议使用dummy节点简化操作),不过单独写函数reverse(node, k)
的好处是,每一次传入的k值都可以不同。
尽量画图辅助理解:
迭代法
调了蛮久终于一次提交通过
class Solution {
public:
ListNode* reverseKGroup(ListNode* head, int k) {
int n = 0;//链表长度
for(auto i = head; i != NULL; i = i->next) ++n;
auto dummy = new ListNode;
dummy->next = head;
auto pre = head, cur = head->next, bef_beg = dummy;
for(int i = 0; i < n/k; ++i){
auto beg = pre;
for(int j = 1; j <= k-1; ++j){//只需调整k-1个指针方向
auto tmp = cur->next;
cur->next = pre;
pre = cur;
cur = tmp;
}
bef_beg->next = pre;
beg->next = cur;
bef_beg = beg;
pre = cur;
if(cur) cur = cur->next;
}
return dummy->next;
}
};
递归法
思路还是和上面一样的,参数可以是首尾节点,也可以是首节点加上k。
此处采取的思路是传入首前、尾后两个节点,具体看代码吧:
reverse函数具体是这样的:
class Solution {
public:
ListNode* reverse(ListNode *bef_beg, ListNode *after_end){//首前,尾后
ListNode* pre = bef_beg->next, *beg = pre;//翻转之后返回beg节点
ListNode* cur = pre->next;
pre->next = after_end;
while(cur != after_end){
auto tmp = cur->next;
cur->next = pre;
pre = cur;
cur = tmp;
}
bef_beg->next = pre;
return beg;
}
ListNode* reverseKGroup(ListNode* head, int k) {
if(!head || !head->next || k == 1) return head;
auto dummy = new ListNode(-1);
dummy->next = head;
auto bef_beg = dummy;
while(bef_beg){
auto end = bef_beg;
for(int i = 0; i < k; ++i){
end = end->next;
if(!end) return dummy->next;
}
bef_beg = reverse(bef_beg, end->next);
}
return dummy->next;
}
};