反转链表问题(递归/迭代)

递归写法

反转整条单链表

可以借助这个写法深入理解递归的精髓 => (总结一下)进入下一层

下一层可以看作一条链表长度 - 1的新链表

ListNode* reverseList(ListNode* head) {
//    这里if => 既是递归的base,返回最后一个结点
//          => 又有对特殊情况的判断,当链表为空或只有一个结点,就没有反转的必要直接返回
    if(!head || !head->next) return head;
    ListNode* last = reverseList(head->next);
//    这行代码就是将该层所处在的结点的下一个结点指向自己
    head->next->next = head;
//    这行实际作用起在一开始链表的头节点 => 反转后变成尾巴啦 => 指向NULL
//    只是从其他地方的结点看这句话没什么用
    head->next = NULL;
    return last;
}

反转链表的任意区间

这里我们先简化问题,将区间左侧固定为头节点

需要注意的就是要记录后继结点 => 方便后续连接

因为这里头节点不是去做尾巴啦,而是中间一个结点,所以要获取后继结点

这里的n是从头节点向后数n个结点

ListNode* successor = NULL; // 后继结点
ListNode* reverseN(ListNode* head, int n) {
    if(n == 1) {
        successor = head->next;
        return head;
    }
    ListNode* last = reverseN(head->next, n-1);
    head->next->next = head;
    head->next = successor;
    return last;
}

处理任意区间 => 不过是将问题转化成我们上述这个比较容易处理的方案

将指针向下遍历至要求的区间左侧的结点,这个时候再去reverseN 

=> 从逆角度去思考就好像需要被反转的区间左侧被固定了

class Solution {
public:
    ListNode* successor = NULL;
    ListNode* reverseN(ListNode* head, int n) {
        if(n == 1) {
            successor = head->next;
            return head;
        }
        ListNode* last = reverseN(head->next, n - 1);
        head->next->next = head;
        head->next = successor;
        return last;
    }

    ListNode* reverseBetween(ListNode* head, int left, int right) {
        if(left == 1) return reverseN(head, right);
        head->next = reverseBetween(head->next, left - 1, right - 1);
        return head;
    }
};

需要注意的是,不要写成下面这种

我知道你是怎么想的,但是你要注意,在递归的过程中,left和right已经改变啦,虽然这是常识,可是在思考问题的时候容易忽略,很正常啦,这个bug代码就是犯过的错误

ListNode* reverseBetween(ListNode* head, int left, int right) {
    if(left == 1) return reverseN(head, left - right + 1);
    head->next = reverseBetween(head->next, left - 1, right);
    return head;
}

迭代写法

反转整条和反转一部分没什么区别其实

=> 反转整条就看成从head->null

先给出反转整条的代码 =>[a, null)

ListNode* reverse(ListNode* a, ListNode* b) {
    ListNode *pre = NULL, *cur = a, *next = a;
    while(cur != NULL) {
        next = cur->next;
        cur->next = pre;
        pre = cur;
        cur = next;
    }
    return pre;
}

只要把while的条件稍加改变 => [a, b)

ListNode* reverse(ListNode* a, ListNode* b) {
    ListNode *pre = NULL, *cur = a, *next = a;
    while(cur != b) {
        next = cur->next;
        cur->next = pre;
        pre = cur;
        cur = next;
    }
    return pre;
}

再来加深一下递归的理解 => 25. K 个一组翻转链表 - 力扣(LeetCode)

ListNode* reverse(ListNode* a, ListNode* b) {
    ListNode *pre = NULL, *cur = a, *next = a;
    while(cur != cur) {
        next = cur->next;
        cur->next = pre;
        pre = cur;
        cur = next;
    }
    return pre;
}

ListNode* reverseKGroup(ListNode* head, int k) {
    if(!head || !head->next) return head;
    ListNode* a = head, *b = head;
    for(int i = 0; i < k; i++) {
//        不足k个一组就不反转
        if(b == NULL) return head;
        b = b->next;
    }
//    根据递归层层返回,最终会得到刚开始反转后的新节点 => 也就是整个反转后的新节点
    ListNode* newHead = reverse(a, b);
//    因为我们的reverse是简化问题后的写法 => 相当于把左侧固定在头节点啦
    a->next = reverseKGroup(b, k);
    return newHead;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值