# Sort List
### Source
- leetcode: [Sort List | LeetCode OJ](https://leetcode.com/problems/sort-list/)
- lintcode: [(98) Sort List](http://www.lintcode.com/en/problem/sort-list/)
~~~
Sort a linked list in O(n log n) time using constant space complexity.
~~~
### 题解1 - 归并排序(链表长度求中间节点)
链表的排序操作,对于常用的排序算法,能达到 O(nlogn)O(n \log n)O(nlogn)的复杂度有快速排序(平均情况),归并排序,堆排序。快速排序不一定能保证其时间复杂度一定满足要求,归并排序和堆排序都能满足复杂度的要求。在数组排序中,归并排序通常需要使用 O(n)O(n)O(n) 的额外空间,也有原地归并的实现,代码写起来略微麻烦一点。但是对于链表这种非随机访问数据结构,所谓的「排序」不过是指针`next`值的变化而已,主要通过指针操作,故仅需要常数级别的额外空间,满足题意。堆排序通常需要构建二叉树,在这道题中不太适合。
既然确定使用归并排序,我们就来思考归并排序实现的几个要素。
1. 按长度等分链表,归并虽然不严格要求等分,但是等分能保证线性对数的时间复杂度。由于链表不能随机访问,故可以先对链表进行遍历求得其长度。
1. 合并链表,细节已在 [Merge Two Sorted Lists | Data Structure and Algorithm](http://algorithm.yuanbin.me/zh-cn/linked_list/merge_two_sorted_lists.html) 中详述。
在按长度等分链表时进行「后序归并」——先求得左半部分链表的表头,再求得右半部分链表的表头,最后进行归并操作。
由于递归等分链表的操作需要传入链表长度信息,故需要另建一辅助函数。新鲜出炉的源码如下。
~~~
/**
* Definition of ListNode
* class ListNode {
* public:
* int val;
* ListNode *next;
* ListNode(int val) {
* this->val = val;
* this->next = NULL;
* }
* }
*/
class Solution {
public:
/**
* @param head: The first node of linked list.
* @return: You should return the head of the sorted linked list,
using constant space complexity.
*/
ListNode *sortList(ListNode *head) {
if (NULL == head) {
return NULL;
}
// get the length of List
int len = 0;
ListNode *node = head;
while (NULL != node) {
node = node->next;
++len;
}
return sortListHelper(head, len);
}
private:
ListNode *sortListHelper(ListNode *head, const int length) {
if ((NULL == head) || (0 >= length)) {
return head;
}
ListNode *midNode = head;
int count = 1;
while (count < length / 2) {
midNode = midNode->next;
++count;
}
ListNode *rList = sortListHelper(midNode->next, length - length / 2);
midNode->next = NULL;
ListNode *lList = sortListHelper(head, length / 2);
return mergeList(lList, rList);
}
ListNode *mergeList(ListNode *l1, ListNode *l2) {
ListNode *dummy = new ListNode(0);
ListNode *lastNode = dummy;
while ((NULL != l1) && (NULL != l2)) {
if (l1->val < l2->val) {
lastNode->next = l1;
l1 = l1->next;
} else {
lastNode->next = l2;
l2 = l2->next;
}
lastNode = lastNode->next;
}
lastNode->next = (NULL != l1) ? l1 : l2;
return dummy->next;
}
};
~~~
### 源码分析
1. 归并子程序没啥好说的了,见 [Merge Two Sorted Lists | Data Structure and Algorithm](http://algorithm.yuanbin.me/zh-cn/linked_list/merge_two_sorted_lists.html).
1. 在递归处理链表长度时,分析方法和 [Convert Sorted List to Binary Search Tree | Data Structure and Algorithm](http://algorithm.yuanbin.me/zh-cn/binary_search_tree/convert_sorted_list_to_binary_search_tree.html) 一致,**`count`表示遍历到链表中间时表头指针需要移动的节点数。**在纸上分析几个简单例子后即可确定,由于这个题需要的是「左右」而不是二叉搜索树那道题需要三分——「左中右」,故将`count`初始化为1更为方便,左半部分链表长度为`length / 2`, 这两个值的确定最好是先用纸笔分析再视情况取初值,不可死记硬背。
1. 找到中间节点后首先将其作为右半部分链表处理,然后将其`next`值置为`NULL`, 否则归并子程序无法正确求解。这里需要注意的是`midNode`是左半部分的最后一个节点,`midNode->next`才是链表右半部分的起始节点。
1. 递归模型中**左、右、合并**三者的顺序可以根据分治思想确定,即先找出左右链表,最后进行归并(因为归并排序的前提是两个子链表各自有序)。
### 复杂度分析
遍历求得链表长度,时间复杂度为 O(n)O(n)O(n), 「折半取中」过程中总共有 log(n)\log(n)log(n) 层,每层找中点需遍历 n/2n/2n/2 个节点,故总的时间复杂度为 n/2⋅O(logn) n/2 \cdot O(\log n)n/2⋅O(logn) (折半取中), 每一层归并排序的时间复杂度介于 O(n/2)O(n/2)O(n/2) 和 O(n)O(n)O(n)之间,故总的时间复杂度为 O(nlogn)O(n \log n)O(nlogn), 空间复杂度为常数级别,满足题意。
### 题解2 - 归并排序(快慢指针求中间节点)
除了遍历链表求得总长外,还可使用看起来较为巧妙的技巧如「快慢指针」,快指针每次走两步,慢指针每次走一步,最后慢指针所指的节点即为中间节点。使用这种特技的关键之处在于如何正确确定快慢指针的起始位置。
### C++
~~~
/**
* Definition of ListNode
* class ListNode {
* public:
* int val;
* ListNode *next;
* ListNode(int val) {
* this->val = val;
* this->next = NULL;
* }
* }
*/
class Solution {
public:
/**
* @param head: The first node of linked list.
* @return: You should return the head of the sorted linked list,
using constant space complexity.
*/
ListNode *sortList(ListNode *head) {
if (NULL == head || NULL == head->next) {
return head;
}
ListNode *midNode = findMiddle(head);
ListNode *rList = sortList(midNode->next);
midNode->next = NULL;
ListNode *lList = sortList(head);
return mergeList(lList, rList);
}
private:
ListNode *findMiddle(ListNode *head) {
if (NULL == head || NULL == head->next) {
return head;
}
ListNode *slow = head, *fast = head->next;
while(NULL != fast && NULL != fast->next) {
fast = fast->next->next;
slow = slow->next;
}
return slow;
}
ListNode *mergeList(ListNode *l1, ListNode *l2) {
ListNode *dummy = new ListNode(0);
ListNode *lastNode = dummy;
while ((NULL != l1) && (NULL != l2)) {
if (l1->val < l2->val) {
lastNode->next = l1;
l1 = l1->next;
} else {
lastNode->next = l2;
l2 = l2->next;
}
lastNode = lastNode->next;
}
lastNode->next = (NULL != l1) ? l1 : l2;
return dummy->next;
}
};
~~~
### 源码分析
1. 异常处理不仅考虑了`head`, 还考虑了`head->next`, 可减少辅助程序中的异常处理。
1. 使用快慢指针求中间节点时,将`fast`初始化为`head->next`可有效避免无法分割两个节点如`1->2->null`[fast_slow_pointer](#)。
- 求中点的子程序也可不做异常处理,但前提是主程序`sortList`中对`head->next`做了检测。
1. 最后进行`merge`归并排序。
****> 在递归和迭代程序中,需要尤其注意终止条件的确定,以及循环语句中变量的自增,以防出现死循环或访问空指针。
### 复杂度分析
同上。
### Reference
- [Sort List | 九章算法](http://www.jiuzhang.com/solutions/sort-list/)
- fast_slow_pointer
> .
[LeetCode: Sort List 解题报告 - Yu's Garden - 博客园](http://www.cnblogs.com/yuzhangcmu/p/4131885.html)[ ↩](# "Jump back to footnote [fast_slow_pointer] in the text.")
- Preface
- Part I - Basics
- Basics Data Structure
- String
- Linked List
- Binary Tree
- Huffman Compression
- Queue
- Heap
- Stack
- Set
- Map
- Graph
- Basics Sorting
- Bubble Sort
- Selection Sort
- Insertion Sort
- Merge Sort
- Quick Sort
- Heap Sort
- Bucket Sort
- Counting Sort
- Radix Sort
- Basics Algorithm
- Divide and Conquer
- Binary Search
- Math
- Greatest Common Divisor
- Prime
- Knapsack
- Probability
- Shuffle
- Basics Misc
- Bit Manipulation
- Part II - Coding
- String
- strStr
- Two Strings Are Anagrams
- Compare Strings
- Anagrams
- Longest Common Substring
- Rotate String
- Reverse Words in a String
- Valid Palindrome
- Longest Palindromic Substring
- Space Replacement
- Wildcard Matching
- Length of Last Word
- Count and Say
- Integer Array
- Remove Element
- Zero Sum Subarray
- Subarray Sum K
- Subarray Sum Closest
- Recover Rotated Sorted Array
- Product of Array Exclude Itself
- Partition Array
- First Missing Positive
- 2 Sum
- 3 Sum
- 3 Sum Closest
- Remove Duplicates from Sorted Array
- Remove Duplicates from Sorted Array II
- Merge Sorted Array
- Merge Sorted Array II
- Median
- Partition Array by Odd and Even
- Kth Largest Element
- Binary Search
- Binary Search
- Search Insert Position
- Search for a Range
- First Bad Version
- Search a 2D Matrix
- Search a 2D Matrix II
- Find Peak Element
- Search in Rotated Sorted Array
- Search in Rotated Sorted Array II
- Find Minimum in Rotated Sorted Array
- Find Minimum in Rotated Sorted Array II
- Median of two Sorted Arrays
- Sqrt x
- Wood Cut
- Math and Bit Manipulation
- Single Number
- Single Number II
- Single Number III
- O1 Check Power of 2
- Convert Integer A to Integer B
- Factorial Trailing Zeroes
- Unique Binary Search Trees
- Update Bits
- Fast Power
- Hash Function
- Count 1 in Binary
- Fibonacci
- A plus B Problem
- Print Numbers by Recursion
- Majority Number
- Majority Number II
- Majority Number III
- Digit Counts
- Ugly Number
- Plus One
- Linked List
- Remove Duplicates from Sorted List
- Remove Duplicates from Sorted List II
- Remove Duplicates from Unsorted List
- Partition List
- Two Lists Sum
- Two Lists Sum Advanced
- Remove Nth Node From End of List
- Linked List Cycle
- Linked List Cycle II
- Reverse Linked List
- Reverse Linked List II
- Merge Two Sorted Lists
- Merge k Sorted Lists
- Reorder List
- Copy List with Random Pointer
- Sort List
- Insertion Sort List
- Check if a singly linked list is palindrome
- Delete Node in the Middle of Singly Linked List
- Rotate List
- Swap Nodes in Pairs
- Remove Linked List Elements
- Binary Tree
- Binary Tree Preorder Traversal
- Binary Tree Inorder Traversal
- Binary Tree Postorder Traversal
- Binary Tree Level Order Traversal
- Binary Tree Level Order Traversal II
- Maximum Depth of Binary Tree
- Balanced Binary Tree
- Binary Tree Maximum Path Sum
- Lowest Common Ancestor
- Invert Binary Tree
- Diameter of a Binary Tree
- Construct Binary Tree from Preorder and Inorder Traversal
- Construct Binary Tree from Inorder and Postorder Traversal
- Subtree
- Binary Tree Zigzag Level Order Traversal
- Binary Tree Serialization
- Binary Search Tree
- Insert Node in a Binary Search Tree
- Validate Binary Search Tree
- Search Range in Binary Search Tree
- Convert Sorted Array to Binary Search Tree
- Convert Sorted List to Binary Search Tree
- Binary Search Tree Iterator
- Exhaustive Search
- Subsets
- Unique Subsets
- Permutations
- Unique Permutations
- Next Permutation
- Previous Permuation
- Unique Binary Search Trees II
- Permutation Index
- Permutation Index II
- Permutation Sequence
- Palindrome Partitioning
- Combinations
- Combination Sum
- Combination Sum II
- Minimum Depth of Binary Tree
- Word Search
- Dynamic Programming
- Triangle
- Backpack
- Backpack II
- Minimum Path Sum
- Unique Paths
- Unique Paths II
- Climbing Stairs
- Jump Game
- Word Break
- Longest Increasing Subsequence
- Palindrome Partitioning II
- Longest Common Subsequence
- Edit Distance
- Jump Game II
- Best Time to Buy and Sell Stock
- Best Time to Buy and Sell Stock II
- Best Time to Buy and Sell Stock III
- Best Time to Buy and Sell Stock IV
- Distinct Subsequences
- Interleaving String
- Maximum Subarray
- Maximum Subarray II
- Longest Increasing Continuous subsequence
- Longest Increasing Continuous subsequence II
- Graph
- Find the Connected Component in the Undirected Graph
- Route Between Two Nodes in Graph
- Topological Sorting
- Word Ladder
- Bipartial Graph Part I
- Data Structure
- Implement Queue by Two Stacks
- Min Stack
- Sliding Window Maximum
- Longest Words
- Heapify
- Problem Misc
- Nuts and Bolts Problem
- String to Integer
- Insert Interval
- Merge Intervals
- Minimum Subarray
- Matrix Zigzag Traversal
- Valid Sudoku
- Add Binary
- Reverse Integer
- Gray Code
- Find the Missing Number
- Minimum Window Substring
- Continuous Subarray Sum
- Continuous Subarray Sum II
- Longest Consecutive Sequence
- Part III - Contest
- Google APAC
- APAC 2015 Round B
- Problem A. Password Attacker
- Microsoft
- Microsoft 2015 April
- Problem A. Magic Box
- Problem B. Professor Q's Software
- Problem C. Islands Travel
- Problem D. Recruitment
- Microsoft 2015 April 2
- Problem A. Lucky Substrings
- Problem B. Numeric Keypad
- Problem C. Spring Outing
- Microsoft 2015 September 2
- Problem A. Farthest Point
- Appendix I Interview and Resume
- Interview
- Resume