【Leetcode】Sort List in O(nlogn) O(1)space

【題目】


【思路】

First of all, allow me to explain the meaning of strict O(1) auxiliary space complexity.

It means the maximum number of memory used by the program, except the memory taken by the input data, doesn't change with the input size. This indicates that, strictly speaking, any solution that involves recursion can never have a strict O(1) auxiliary space complexity. Because the maximum recursion level depends on the the input size and each recursion call consumes memory on stack, thus the maximum number of memory used depends on the input size.

Secondly, allow me to explain my solution based on merge sort.

Assume the length of list is n, which is unknown at the beginning.

  level log(n)         [ A0, A2, A3, ... , An-2, An-1 ]
  ...
  level 2        [ A0, A1, A2,  A3 ], [A4, A5, A6, A7] ,... , [ ..., An-2, An-1 ]
  level 1        [ A0, A1], [A2,  A3 ], [A4, A5], [A6, A7] ,... , [An-2, An-1]
  level 0        [ A0 ], [ A1] , [ A2 ],  [ A3 ],... , [ An-2 ], [ An-1 ]

At each level, each group only contains at maximum 2^level elements. Merge-sort theses groups pair by pair. Then level ++. Stop until 2^level > n. Assume the original input is :

  level 0        5, 3, 6, 1, 4, 2, 7

After level 0, we got the length of the list and the list become:

  level 1        3, 5,   1, 6,    2, 4,    7

Now each group contains 2 elements. After level 1, the list become:

  level 2        1, 3, 5, 6,    2, 4, 7

Now each group contains 2^2 = 4 elements. After level 2, the list become:

  level 3        1, 2, 3, 4, 5, 6, 7

Now, 2^3 > 7, stop.

Time complexity: In each level, each node is visited by at maximum twice. And there are log(n) level. Thus the time complexity is O(2n* log n ) => O( n* log n )




【代碼】

public class Solution {
private class MergeHelper {
        public ListNode newHead;
        public ListNode newTail;
}
public ListNode sortList(ListNode head) {
    if ( head == null || head.next == null) {
        return head;
    }

    ListNode dummyHeadOne = new ListNode(0);
    ListNode dummyHeadTwo = new ListNode(0);
    ListNode dummySortedHead = new ListNode(0);
    ListNode dummySortedLast = dummySortedHead;
    ListNode unvisitedNode = head;
    MergeHelper mergeRst = new MergeHelper();

    int listLength = 0;
    int level = 0;
    while ( unvisitedNode != null && unvisitedNode.next != null ) {
        unvisitedNode = addNode ( dummyHeadOne, unvisitedNode, 1<<level);
        unvisitedNode = addNode ( dummyHeadTwo, unvisitedNode, 1<<level);
        merge ( dummyHeadOne.next, dummyHeadTwo.next, mergeRst);
        dummySortedLast.next = mergeRst.newHead;
        dummySortedLast = mergeRst.newTail;
        listLength += 2;
    }
    if (unvisitedNode != null) {
        dummySortedLast.next = unvisitedNode;
        listLength ++;
    }
    level ++;

    while ( listLength > 1 << level) {
        dummySortedLast = dummySortedHead;
        unvisitedNode = dummySortedHead.next;
        while (unvisitedNode != null) {
            unvisitedNode = addNode ( dummyHeadOne, unvisitedNode, 1<<level);
            unvisitedNode = addNode ( dummyHeadTwo, unvisitedNode, 1<<level);
            merge ( dummyHeadOne.next, dummyHeadTwo.next, mergeRst);
            dummySortedLast.next = mergeRst.newHead;
            dummySortedLast = mergeRst.newTail;
        }
        level ++;
    }

    return dummySortedHead.next;
}

/* merge listOne and listTwo. 
Save the sorted list head into rst.newHead
Save the last node of the sorted list into rst.newTail
*/
private void merge (ListNode listOne, ListNode listTwo, MergeHelper rst) {
    ListNode dummyHead = new ListNode (0);
    ListNode lastNode = dummyHead;
    while (listOne != null && listTwo != null) {
        if ( listOne.val < listTwo.val ) {
            lastNode.next = listOne;
            listOne = listOne.next;
        } else {
            lastNode.next = listTwo;
            listTwo = listTwo.next;
        }
        lastNode = lastNode.next;
    }

    while (listOne != null) {
        lastNode.next = listOne;
        listOne = listOne.next;
        lastNode = lastNode.next;
    }
    while ( listTwo != null ) {
        lastNode.next = listTwo;
        listTwo = listTwo.next;
        lastNode = lastNode.next;
    }
    rst.newHead = dummyHead.next;
    rst.newTail = lastNode;
}

/*
 add at max #"count" nodes into "head" from "source"
 return the new position of source after adding.
*/
private ListNode addNode ( ListNode head, ListNode source, int count ) {
    while (count > 0 && source != null) {
        head.next = source;
        head = head.next;
        source = source.next;
        count --;
    }
    head.next = null;
    return source;
}}


pre的作用就是爲了找到第一個sublist的結尾,然後斷開和第二個sublist的連接。

public class Solution {

    //merge two sorted list, return result head
    public ListNode merge(ListNode h1, ListNode h2){
        if(h1 == null){
            return h2;
        }
        if(h2 == null){
            return h1;
        }

        if(h1.val < h2.val){
            h1.next = merge(h1.next, h2);
            return h1;
        }
        else{
            h2.next = merge(h1, h2.next);
            return h2;
        }

    }

    public ListNode sortList(ListNode head) {
        //bottom case
        if(head == null){
            return head;
        }
        if(head.next == null){
            return head;
        }

        //p1 move 1 step every time, p2 move 2 step every time, pre record node before p1
        ListNode p1 = head;
        ListNode p2 = head;
        ListNode pre = head;

        while(p2 != null && p2.next != null){
            pre = p1;
            p1 = p1.next;
            p2 = p2.next.next;
        }
        //change pre next to null,<span style="background-color: rgb(255, 204, 204);"> make two sub list</span>(head to pre, p1 to p2)
      <span style="background-color: rgb(255, 204, 153);">  pre.next = null;</span>

        //handle those two sub list
        ListNode h1 = sortList(head);
        ListNode h2 = sortList(p1);

        return merge(h1, h2);

    }

}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章