题目描述(中等难度)

148. Sort List - 图1

要求时间复杂度为 O(nlogn),最常用的就是归并排序了。

解法一

归并排序需要一个辅助方法,也就是对两个有序链表进行合并,在 21 题 已经讨论过。

至于归并排序的思想,这里就不多讲了,本科的时候用 Scratch 做过一个演示视频,感兴趣的可以参考 这里,哈哈。

那就直接放代码了。因为归并排序是一半一半的进行,所以需要找到中点。最常用的方法就是快慢指针去找中点了。

  1. ListNode dummy = new ListNode(0);
  2. dummy.next = head;
  3. ListNode fast = dummy;
  4. ListNode slow = dummy;
  5. while (fast != null && fast.next != null) {
  6. slow = slow.next;
  7. fast = fast.next.next;
  8. }

上边的代码我加了一个 dummy 指针,就是想当节点个数是偶数的时候,让 slow 刚好指向前边一半节点的最后一个节点,也就是下边的状态。

  1. 1 2 3 4
  2. ^ ^
  3. slow fast

如果 slowfast 都从 head 开始走,那么当 fast 结束的时候,slow 就会走到后边一半节点的开头了。

当然除了上边的方法,在 这里 看到,还可以加一个 pre 指针,让它一直指向 slow 的前一个即可。

  1. // step 1. cut the list to two halves
  2. ListNode prev = null, slow = head, fast = head;
  3. while (fast != null && fast.next != null) {
  4. prev = slow;
  5. slow = slow.next;
  6. fast = fast.next.next;
  7. }

他们的目的都是一样的,就是为了方便的把两个链表平均分开。

  1. public ListNode sortList(ListNode head) {
  2. return mergeSort(head);
  3. }
  4. private ListNode mergeSort(ListNode head) {
  5. if (head == null || head.next == null) {
  6. return head;
  7. }
  8. ListNode dummy = new ListNode(0);
  9. dummy.next = head;
  10. ListNode fast = dummy;
  11. ListNode slow = dummy;
  12. //快慢指针找中点
  13. while (fast != null && fast.next != null) {
  14. slow = slow.next;
  15. fast = fast.next.next;
  16. }
  17. ListNode head2 = slow.next;
  18. slow.next = null;
  19. head = mergeSort(head);
  20. head2 = mergeSort(head2);
  21. return merge(head, head2);
  22. }
  23. private ListNode merge(ListNode head1, ListNode head2) {
  24. ListNode dummy = new ListNode(0);
  25. ListNode tail = dummy;
  26. while (head1 != null && head2 != null) {
  27. if (head1.val < head2.val) {
  28. tail.next = head1;
  29. tail = tail.next;
  30. head1 = head1.next;
  31. } else {
  32. tail.next = head2;
  33. tail = tail.next;
  34. head2 = head2.next;
  35. }
  36. }
  37. if (head1 != null) {
  38. tail.next = head1;
  39. }
  40. if (head2 != null) {
  41. tail.next = head2;
  42. }
  43. return dummy.next;
  44. }

当然严格的说,上边的解法空间复杂度并不是 O(1),因为递归过程中压栈是需要消耗空间的,每次取一半,所以空间复杂度是 O(log(n))

递归可以去改写成迭代的形式,也就是自底向上的走,就可以省去压栈的空间,空间复杂度从而达到 O(1),详细的可以参考 这里-with-o(1)-space-complextity-and-o(nlgn)-time-complextity) 。

147 题 一样,主要还是考察对链表的理解和排序算法的实现。

windliang wechat

添加好友一起进步~

如果觉得有帮助的话,可以点击 这里 给一个 star 哦 ^^

如果想系统的学习数据结构和算法,强烈推荐一个我之前学过的课程,可以点击 这里 查看详情