跳转至

143.Reorder List

Tags: Medium Linked List

Links: https://leetcode.com/problems/reorder-list/


Given a singly linked list L: L*0→*L*1→…→*L**n-1→L*n, reorder it to: *L*0→*L**nL*1→*L**n-1→L*2→*L**n-2→…

You may not modify the values in the list's nodes, only nodes itself may be changed.

Example 1:

Given 1->2->3->4, reorder it to 1->4->2->3.

Example 2:

Given 1->2->3->4->5, reorder it to 1->5->2->4->3.

最直接的思路,类似于删除倒数第k个数,先找出要插入的部分,注意要插入的部分是倒过来的,所以中间需要一步翻转链表。翻转完成之后就是两个链表的合并了。思路很直接,但是速度很不理想。

Runtime: 784 ms, faster than 6.00% of C++ online submissions for Reorder List.
Memory Usage: 12.2 MB, less than 82.35% of C++ online submissions for Reorder List.
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    void reorderList(ListNode* head) {
        //空链表或链表只有一个或两个元素节点
        if (!head || !head -> next || !head -> next -> next) return;

        //保证了链表里至少三个节点
        ListNode * dummy1 = new ListNode(0);
        dummy1 -> next = head;

        int cnt = 0; //记录链表长度
        ListNode * cur = dummy1, *pre = dummy1;
        while(cur -> next){
            ++cnt;
            cur = cur -> next;
        }

        int half = 0;//需要插入的链表长度
        if (cnt % 2 == 0) half = cnt / 2 - 1;
        else half = cnt / 2; 

        cur = head;
        ListNode *prob = cur;
        do{
            for (int i = 1; i <= half - 1; ++i)
                prob = prob -> next;

            if (!prob -> next) break;
            cur = cur -> next;
            pre = pre -> next;
            prob = cur;
        } while(true);

        //把原来的链断开
        pre -> next = nullptr;
        //尾部的链需要翻转
        ListNode * dummy2 = new ListNode(0);
        dummy2 -> next = cur;
        pre = cur;
        cur = cur -> next;
        while(cur) {
            pre -> next = cur -> next;
            cur -> next = dummy2 -> next;
            dummy2 -> next = cur;
            cur = pre -> next;
        }

        //两个链合并
        pre = head;
        cur = pre -> next;
        prob = dummy2 -> next;
        while (dummy2 -> next) {
            dummy2 -> next = prob -> next;
            prob -> next = cur;
            pre -> next = prob;
            pre = pre -> next -> next;
            cur = pre -> next;
            prob = dummy2 -> next;
        }
    }
};

速度慢的原因根本在于查找断开的位置部分,如果链表很长,那么就需要反复探测。所以改进的方案就是快慢指针,很明显我们需要在中间断开,利用快慢指针就不用再考虑长度是奇数还是偶数了。

改进后的方案:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    void reorderList(ListNode* head) {
        //空链表或链表只有一个或两个元素节点
        if (!head || !head -> next || !head -> next -> next) return;

        //保证了链表里至少三个节点
        ListNode * dummy1 = new ListNode(0);
        dummy1 -> next = head;

        //快慢指针从中间断开
        ListNode *pre = head, *cur = head;
        while (cur && cur -> next) {
            pre = pre -> next;
            cur = cur -> next -> next;
        }

        //把原来的链断开
        cur = pre -> next;
        pre -> next = nullptr;
        //尾部的链需要翻转
        ListNode * dummy2 = new ListNode(0);
        dummy2 -> next = cur;
        pre = cur;
        cur = cur -> next;
        while(cur) {
            pre -> next = cur -> next;
            cur -> next = dummy2 -> next;
            dummy2 -> next = cur;
            cur = pre -> next;
        }

        //两个链合并
        pre = head;
        cur = pre -> next;
        ListNode * prob = dummy2 -> next;
        while (dummy2 -> next) {
            dummy2 -> next = prob -> next;
            prob -> next = cur;
            pre -> next = prob;
            pre = pre -> next -> next;
            cur = pre -> next;
            prob = dummy2 -> next;
        }
    }
};

速度明显加快了!

Runtime: 48 ms, faster than 78.36% of C++ online submissions for Reorder List.
Memory Usage: 12 MB, less than 100.00% of C++ online submissions for Reorder List.