92. Reverse Linked List II**

92. Reverse Linked List II**

https://leetcode.com/problems/reverse-linked-list-ii/

题目描述

Reverse a linked list from position m to n. Do it in one-pass.

Note: 1 ≤ m ≤ n ≤ length of list.

Example:

Input: 1->2->3->4->5->NULL, m = 2, n = 4
Output: 1->4->3->2->5->NULL

C++ 实现 1

朴素的思路. 将整个过程分为三步:

  1. 对于 i < m 的节点, 按顺序挂载到 dummy 链表中;
  2. 对于 m <= i <= n 的节点, 进行翻转
  3. 对于 i > n 的节点, 按顺序挂载到 dummy 链表中;
class Solution {
private:
    ListNode* reverse(ListNode *head) {
        ListNode *prev = nullptr;
        while (head) {
            auto tmp = head->next;
            head->next = prev;
            prev = head;
            head = tmp;
        }
        return prev;
    }
public:
    ListNode* reverseBetween(ListNode* head, int m, int n) {
        ListNode *dummy = new ListNode(0);
        auto p = dummy;
        int i = 1;
        // 第一步
        while (i < m) {
            p->next = head;
            p = p->next;
            head = head->next;
            ++ i;
        }
        // 第二步, start 指向 m <= i <= n 的节点, 注意最末尾的指针指向 nullptr
        auto start = head;
        while (i < n) {
            head = head->next;
            ++ i;
        }
        auto tmp = head->next;
        head->next = nullptr;
        head = tmp;
        p->next = reverse(start);
        // 第三步
        while (p->next) p = p->next;
        p->next = head;
        return dummy->next;
    }
};

C++ 实现 2

使用 start 找到第 m 个节点的前一个节点, 使用 end 找到第 n 个节点的后一个节点. 然后使用 reverse 函数翻转 start->nextend 之间的节点. 对常规的 reverse 函数进行了修改. 嗯, 两年前的代码.

现在再想想, reverse 函数中 tail 接收的是第 n 个节点的后一个节点, 是非常符合直觉的, 看我在 206. Reverse Linked List* 中对 reverse 的实现方法, 其中 prev 初始化为 nullptr, 也可以看成是要翻转的链表中的最后一个节点的下一个节点.

class Solution {
private:
    ListNode* reverse(ListNode *root, ListNode *tail) {
        auto prev = tail;
        while (root != tail) {
            auto tmp = root->next;
            root->next = prev;
            prev = root;
            root = tmp;
        }
        return prev;
    }
public:
    ListNode* reverseBetween(ListNode *head, int m, int n) {
        if (!head || !head->next) return head;
        auto dummy = new ListNode(0);
        dummy->next = head;
        int count = 0;
        ListNode *ptr = dummy;
        ListNode *start, *end;
        while (ptr) {
            count ++;
            if (count == m)
                start = ptr;
            if (count == n + 1) // 由于ptr初始值指向 dummy, 所以 count 要等于 n+1
                end = ptr->next;
            ptr = ptr->next;
        }
        start->next = reverse(start->next, end);
        return dummy->next;
    }
};

C++ 实现 3

引入虚拟节点 dummy, 另外 p 指向当前访问节点的前一个节点, 比如 [1, 2, 3, 4, 5], left=2, right=4 这个例子, 最终的效果是 prev 指向 1, tail 指向 5.

代码中 tail = p->next->next; 这一句之所以不需要判断 p->next 是否为空在于两点, 首先 right 节点按题目要求中说的, 是存在的, 另一方面, p 始终指向当前访问节点的前一节点; 由于当前访问的 right 节点存在, 即 p->next 存在, 因此可以直接访问 p->next.

class Solution {
private:
    ListNode* reverse(ListNode *head, ListNode *tail) {
        ListNode *prev = tail;
        while (head != tail) {
            auto tmp = head->next;
            head->next = prev;
            prev = head;
            head = tmp;
        }
        return prev;
    }
public:
    ListNode* reverseBetween(ListNode* head, int left, int right) {
        ListNode *prev, *tail;
        int i = 0;
        ListNode *dummy = new ListNode(0);
        dummy->next = head;
        auto p = dummy;
        while (p) {
            i += 1;
            if (i == left) prev = p;
            if (i == right) {
                tail = p->next->next;
                break;
            }
            p = p->next;
        }
        prev->next = reverse(prev->next, tail);
        return dummy->next;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值