leetcode中拍案惊奇的链表题

分列如下:

  • 142.Linked List Cycle II
  • 148.Sort List
  • 143.Reorder List

142. Linked List Cycle II

https://leetcode.com/problems/linked-list-cycle-ii/description/

题意

Given a linked list, return the node where the cycle begins. If there is no cycle, return null.

Note: Do not modify the linked list.

Follow up:
Can you solve it without using extra space?

思路

O(n)时间复杂度,O(1)空间复杂度就找到了链表中环的第一个node。拍案叫绝!解析参考这里https://www.cnblogs.com/hiddenfox/p/3408931.html

代码

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode* fast = head;
        ListNode* slow = head;
        do{
            if(fast == NULL) return NULL;
            fast = fast ->next;
            slow = slow ->next;
            if(fast == NULL) return NULL;
            fast = fast->next;
            if(fast == slow) break;
        }while(true);
        slow = head;
        while(slow != fast) {
            slow = slow->next;
            fast = fast->next;
        }
        return slow;
    }
};

148. Sort List

https://leetcode.com/problems/sort-list/description/

题意

给出一个单向链表,要求排序,时间复杂度为O(nlgn),空间复杂度为O(1)。

思路

一般的思路是归并排序,用到递归。这样,其实在运行的过程中,就需要建立函数栈,实际空间复杂度并不是O(1)。
令人拍案惊奇的一个方法就是用循环来代替递归,从而将空间复杂度真正降到O(1)。
参考https://leetcode.com/problems/sort-list/discuss/46712/Bottom-to-up(not-recurring)-with-o(1)-space-complextity-and-o(nlgn)-time-complextity
这个答案代码十分好的一点在于,将split和merge函数独立出来,因此思路十分清晰,读者第一眼就能知道思路。

参考代码

// 代码转载自https://leetcode.com/problems/sort-list/discuss/46712/Bottom-to-up(not-recurring)-with-o(1)-space-complextity-and-o(nlgn)-time-complextity
/**
 * Merge sort use bottom-up policy, 
 * so Space Complexity is O(1)
 * Time Complexity is O(NlgN)
 * stable sort
*/
class Solution {
public:
    ListNode *sortList(ListNode *head) {
        if(!head || !(head->next)) return head;

        //get the linked list's length
        ListNode* cur = head;
        int length = 0;
        while(cur){
            length++;
            cur = cur->next;
        }

        ListNode dummy(0);
        dummy.next = head;
        ListNode *left, *right, *tail;
        for(int step = 1; step < length; step <<= 1){
            cur = dummy.next;
            // 取地址,而不是引用,注意了
            tail = &dummy;
            while(cur){
                left = cur;
                right = split(left, step);
                cur = split(right,step);
                tail = merge(left, right, tail);
            }
        }
        return dummy.next;
    }
private:
    /**
     * Divide the linked list into two lists,
     * while the first list contains first n ndoes
     * return the second list's head
     */
    ListNode* split(ListNode *head, int n){
        //if(!head) return NULL;
        for(int i = 1; head && i < n; i++) head = head->next;

        if(!head) return NULL;
        ListNode *second = head->next;
        head->next = NULL;
        return second;
    }
    /**
      * merge the two sorted linked list l1 and l2,
      * then append the merged sorted linked list to the node head
      * return the tail of the merged sorted linked list
     */
    ListNode* merge(ListNode* l1, ListNode* l2, ListNode* head){
        ListNode *cur = head;
        while(l1 && l2){
            if(l1->val > l2->val){
                cur->next = l2;
                cur = l2;
                l2 = l2->next;
            }
            else{
                cur->next = l1;
                cur = l1;
                l1 = l1->next;
            }
        }
        cur->next = (l1 ? l1 : l2);
        while(cur->next) cur = cur->next;
        return cur;
    }
};

143. Reorder List

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

题意

Given a singly linked list L: L0→L1→…→Ln-1→Ln,
reorder it to: L0→Ln→L1→Ln-1→L2→Ln-2→…

You must do this in-place without altering the nodes’ values.

For example,
Given {1,2,3,4}, reorder it to {1,4,2,3}.

题意

这是一道常规的题目
我们可以看到,我们需要三个操作就可以得到结果。分别是:
1. split, 将输入链表切成两半
2. reverse, 将后一半链表逆序
3. merge,将前一半数组和逆序后的后一半数组交替连接。

但是最美丽的地方在于,我们参考148. Sort 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) return;
        int len = 0;
        ListNode* cur = head, *second;
        while(cur != NULL){
            len++;
            cur = cur->next;
        }
        int lenOfFirst = len/2 + len%2;
        second = split(head, lenOfFirst);
        second = reverse(second);
        merge(head, second);
        return;
    }

    ListNode* split(ListNode* head, int lenOfFirst){
        ListNode* second, *cur = head;
        for(int i = 1; i < lenOfFirst; i++) {
            cur = cur->next;
        }
        second = cur->next;
        cur->next = NULL;
        return second;
    }

    ListNode* reverse(ListNode* head) {
        if(!head || !head->next) return head;
        ListNode* cur = head->next, *tmp;
        head->next = NULL;
        while(cur != NULL) {
            tmp = cur->next;
            cur->next = head;
            head = cur;
            cur = tmp;
        }
        return head;
    }

    void merge(ListNode* first, ListNode* second) {
        ListNode* cur = first;
        first = first->next;

        while(first) {
            cur->next = second;
            second = second->next;
            cur = cur->next;
            cur->next = first;
            first = first->next;
            cur = cur->next;
        }
        cur->next = first?first:second;
        return;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值