148. Sort List
题目描述和难度
- 题目描述:
在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序。
示例 1:
输入: 4->2->1->3 输出: 1->2->3->4
示例 2:
输入: -1->5->3->4->0 输出: -1->0->3->4->5
- 题目难度:中等。
- 英文网址:148. Sort List 。
- 中文网址:148. 排序链表 。
思路分析
求解关键:题目中已经提示了,要在 O(n log n) 时间复杂度下完成单链表的排序,那么归并排序就是一个很不错的选择。归并排序有自上而下和自下而上的归并排序,它们的区别是:自上而下的归并排序是待排序的子数组越来越小的过程,而自下而上的归并排序是待归并的子数组越来越大的过程。下面我们就分别介绍这两种思路。
- 思路1:自上而下的归并排序(对应参考解答1)
要使用自底向上的归并排序,就要找到链表中间的那个元素,一个宝贵的经验就是:维护两个指针,一快一慢。快指针每次后移两个位置,慢指针每次只移动一个位置。当快指针移动到链表的结尾或者最后一个有效结点时,慢指针就指向了中间的节点。
- 思路2:自下而上的归并排序(对应参考解答2) 下面的图展示了自下而上进行单链表的归并排序的过程:
参考解答
参考解答1
class ListNode {
int val;
ListNode next;
ListNode(int x) {
val = x;
}
ListNode(int[] nums) {
ListNode currNode = this;
currNode.val = nums[0];
for (int i = 1; i < nums.length; i++) {
currNode.next = new ListNode(nums[i]);
currNode = currNode.next;
}
}
@Override
public String toString() {
ListNode currNode = this;
StringBuilder s = new StringBuilder();
while (currNode != null) {
s.append(currNode.val);
s.append(" -> ");
currNode = currNode.next;
}
s.append("NULL");
return s.toString();
}
}
public class Solution {
public ListNode sortList(ListNode head) {
// 递归终止的条件,即满足下面条件就不用找中点,可以直接返回
if (head == null || head.next == null) {
return head;
}
// 使用归并排序、分治思想,先要找到链表的中间结点
ListNode fast = head;
ListNode slow = head;
// 下面这段代码是找链表中间结点的一般做法
while (fast.next != null && fast.next.next != null) {
slow = slow.next;
fast = fast.next.next;
}
// 定义位于中间结点的下一个结点,从它那里将一个链表切开
ListNode midNext = slow.next;
// 这里一定要记得从中间切开,分割成两个链表
slow.next = null;
ListNode listNodeLeft = sortList(head);
ListNode listNodeRight = sortList(midNext);
// 合并两个已经排序的单链表,这是我们很熟悉的操作了
return mergeOfTwoSortListNode(listNodeLeft, listNodeRight);
}
private ListNode mergeOfTwoSortListNode(ListNode l1, ListNode l2) {
if (l1 == null) {
return l2;
}
if (l2 == null) {
return l1;
}
if (l1.val < l2.val) {
l1.next = mergeOfTwoSortListNode(l1.next, l2);
return l1;
} else {
l2.next = mergeOfTwoSortListNode(l1, l2.next);
return l2;
}
}
public static void main(String[] args) {
int[] nums = new int[]{4, 2, 1, 3};
ListNode head = new ListNode(nums);
Solution solution = new Solution();
ListNode sortList = solution.sortList(head);
System.out.println(sortList);
}
}
参考解答2
/**
* 自下而上进行归并
*
* @author liwei
*/
public class Solution2 {
public ListNode sortList(ListNode head) {
if (head == null || head.next == null) {
return head;
}
// 这里设置 64 ,是一个戳戳有余的数字,可以满足结点数量为 2^64 这么多的单链表的排序
ListNode[] counter = new ListNode[64];
ListNode curNode = head;
// 遍历到的最大的 counter 数组的索引
int maxIndex = 0;
while (curNode != null) {
// 先把当前元素暂存起来,稍候要把它放到 counter 数组合适的位置上
ListNode carryNode = curNode;
// curNode 指针马上后移,方便下次处理
curNode = curNode.next;
// 拿出的节点就和原来的链表没有关系了,我们在 counter 数组中完成排序,所以要切断它和原链表的关系
carryNode.next = null;
// 尝试从 counter 数组 0 号索引开始放置
int i = 0;
// 只要非空当前位置非空,就进行一次 merge,merge 以后尝试放到下一格,如果下一格非空就继续合并
// 合并以后再尝试放到下一格,直到下一格为空,直接放在那个为空的下一格就好
while (counter[i] != null) {
ListNode newMergeNode = mergeOfTwoSortedListNode(carryNode, counter[i]);
counter[i] = null;
i++;
carryNode = newMergeNode;
}
// 遇到了空,就把 carryNode 放在数组的这个位置上
counter[i] = carryNode;
// 记录最多使用到 counter 数组的第几位,最后合并的时候要用上
if (i > maxIndex) {
maxIndex = i;
}
}
// 遍历整个 count 数组,将它们全部归并,这个操作就和归并 n 个有序单链表是一样的了,我们这里采用两两归并
// 还可以采用 LeetCode 第 23 题的办法完成这一步
// 参考:https://liweiwei1419.github.io/leetcode-solution/leetcode-0023-merge-k-sorted-lists/
ListNode res = null;
for (int i = 0; i <= maxIndex; i++) {
if (counter[i] != null) {
res = mergeOfTwoSortedListNode(res, counter[i]);
}
}
return res;
}
/**
* 归并两个已经排好序的单链表,是我们非常熟悉的操作了,可以递归完成,也可以穿针引线,这里我们递归完成
*
* @param l1 顺序存放的单链表1
* @param l2 顺序存放的单链表2
* @return 合并以后的单链表
*/
private ListNode mergeOfTwoSortedListNode(ListNode l1, ListNode l2) {
if (l1 == null) {
return l2;
}
if (l2 == null) {
return l1;
}
if (l1.val < l2.val) {
l1.next = mergeOfTwoSortedListNode(l1.next, l2);
return l1;
} else {
l2.next = mergeOfTwoSortedListNode(l1, l2.next);
return l2;
}
}
}
参考资料:
1. http://www.cnblogs.com/bin3/articles/1858691.html
2. https://blog.csdn.net/qq575787460/article/details/40706747
本篇文章的地址为 https://liweiwei1419.github.io/leetcode-solution/leetcode-0148-sort-list ,如果我的题解有错误,或者您有更好的解法,欢迎您告诉我 liweiwei1419@gmail.com 。