Sort List

update Jul,26 2017 21:57

LintCodearrow-up-right LeetCodearrow-up-right

Sort a linked list in O(n log n) time using constant space complexity.

Example

Given 1->3->2->null, sort it to 1->2->3->null.

Challenge Solve it by merge sort & quick sort separately.

Basic Idea:

Merge Sort: 对链表进行 Merge Sort 的精髓在于 的方法:getMid,然后断开mid之后的链。然后使用merge two sorted list 将两端merge。在合并的时候可以先新建dummy node指向head,然后从dummy开始,可以让code更concise。

Quick Sort 如果交换 node 本身会让问题变得复杂,所以精髓是只交换值而不交换 node。注判断递归出口的细节,要加上当head == tail.next 时,直接return。

Java Code:

Merge sort:

    /**
     * Definition for ListNode.
     * public class ListNode {
     *     int val;
     *     ListNode next;
     *     ListNode(int val) {
     *         this.val = val;
     *         this.next = null;
     *     }
     * }
     */ 

     merge sort solution
     public class Solution {
         /**
          * @param head: The head of linked list.
          * @return: You should return the head of the sorted linked list,
                         using constant space complexity.
          */

         // merge sort
        public ListNode sortList(ListNode head) {  
            return mergeSort(head);
        }
        private ListNode mergeSort(ListNode start) {
            if (start == null) return null;
            if (start.next == null) return start;
            ListNode mid = getMid(start);
            ListNode next = mid.next;
            mid.next = null;
            ListNode left_start = mergeSort(start);
            ListNode right_start = mergeSort(next);
            return merge(left_start, right_start);
        }
        private ListNode getMid(ListNode start) {
            ListNode slow = start;
            ListNode fast = start.next;
            while (fast != null && fast.next != null) {
                slow = slow.next;
                fast = fast.next.next;
            }
            return slow;
        }
        private ListNode merge(ListNode left, ListNode right) {
            if (left == null) return right;
            if (right == null) return left;
            ListNode dummy = new ListNode(0);
            ListNode curr = dummy;
            while (left != null || right != null) {
                if (left == null) {
                    curr.next = right;
                    break;
                } 
                if (right == null) {
                    curr.next = left;
                    break;
                }
                if (left.val < right.val) {
                    curr.next = left;
                    curr = curr.next;
                    left = left.next;
                } else {
                    curr.next = right;
                    curr = curr.next;
                    right = right.next;
                }
            }
            return dummy.next;
        }
    }

Quick Sort:

Python Code:

Merge Sort:

Quick Sort:

update Nov 26, 2017

重写此题

时隔数月,重写此题果然有了新的感悟。

  1. 首先,对于 merge sort:

    对于mergesort来说,最重要的是实现先分开再merge。之前的写法是先用一个getMid函数找到中间node,再将其与后部断开,如此得到两段list。这次我把断开两段list的代码放在了 getMid 函数中(split函数),使得code更加简洁。 另外,这次的merge函数也比上次更简洁了。

  2. 对于 quick sort:

    终于写出了基于移动 node 本身的 quick sort。先说一下实现思路:

    整个思路的核心是 partition 的方法,这里采用的方法和之前的 Partition List 的思路非常接近,不同的是这里的 partition 不只考虑一整个list,而是一个list中的一段。在我的实现中,partition function takes preHead 和 tailNext 两个参数,即需要做partition的list左右与其相邻的node。具体的,仍然是将 <= pivot 的node都放到 dummy1 之后,> pivot 的node都放到dummy2之后,最终再链接回原来的地方。

    整个实现过程细节很多,个人感觉比较难一次写对,而且由于是recursion,很可能会陷入非常复杂的 debug。不过整个过程对于我的 debug 能力又是一次提高,这里写一下感悟: 1. 链表的问题一定要特别注意想办法保持 prev node,这里写 partition 的时候,索性将两个参数都定为了 prevHead 和 tailNext; 2. 写这类题目之前一定要在纸上列好框架,对于边界情况或者递归 base case 需要画出示意图;例如这道题中我写到quicksort时,前面需要考虑preHead和tailNext两个参数在什么情况下是 base case。写到 partition 又需要注意在将两个 partition 好的链表重新连接回原来位置的时候会有某个链表是空的(即pivot恰好是最值);

Java Code:

Merge Sort

Quick Sort

Python Code

Merge Sort

Quick Sort