还原成数组结构借助内部排序方法显然不是面试官所期待的解法;
链表结构中模拟快排显然很复杂,因此想能不能用归并;
很容易将问题拆解成:查找中间结点、分治母链表、合并两个链表的子问题;
问题的难点在于如何在merge后保持链表的完整性,因此需要注意以下几个细节:
- 由于合并之后的链表的头结点可能和原head不一致,因此merge是有ListNode类型的返回值的;
- merge所返回的是合并后的链表,而在mergeSort中,这相当于左右两个子链表;
- 为了使merge完全转化为合并两个链表的问题,要确保两个子链表的尾部都指向null,而具体的实现是将中间结点的next指向null;
- 递归的终止条件在于分治到只有一个结点的子链表,此时
mid = head; mid.next = null
,再进入下一层递归时要出来,显然终止条件应该是head.next == null
,而返回的是head
,不是null
;
- 合并链表的时候由于头结点要发生改变,因此最好的方式是建立一个哨兵;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
|
class Solution { public ListNode sortList(ListNode head) { if (head == null) return null; ListNode res = mergeSort(head); return res; } private ListNode mergeSort(ListNode head) { if (head.next == null) return head; ListNode mid = midNode(head); ListNode p = head, q = mid.next; mid.next = null; ListNode left = mergeSort(p); ListNode right = mergeSort(q); return merge(left, right); } private ListNode merge(ListNode p, ListNode q) { ListNode sentry = new ListNode(0); ListNode cur = sentry; while (p != null && q != null) { if (p.val < q.val) { cur.next = p; p = p.next; } else { cur.next = q; q = q.next; } cur = cur.next; } if (p == null) { cur.next = q; } else { cur.next = p; } return sentry.next; } private ListNode midNode(ListNode head) { ListNode slow = head; ListNode fast = head.next; while (fast != null && fast.next != null) { slow = slow.next; fast = fast.next.next; } return slow; } }
|