题目描述

image.png

解题思路

暴力

遍历后排序,在生成链表,空间复杂度不满足常数级

  1. public ListNode sortList(ListNode head) {
  2. if (head == null) return null;
  3. ListNode cur = head;
  4. List<Integer> list = new ArrayList<>();
  5. while (cur != null) {
  6. list.add(cur.val);
  7. cur = cur.next;
  8. }
  9. Collections.sort(list);
  10. ListNode head1 = new ListNode(list.get(0));
  11. cur = head1;
  12. for (int i = 1; i < list.size(); i++) {
  13. cur.next = new ListNode(list.get(i));
  14. cur = cur.next;
  15. }
  16. return head1;
  17. }

归并排序

image.png

递归版

详解参照🔗
归并排序的思路就是将链表一分为二,一直分割到1个链表才进行合并,分割采用前后指针的方法找出中间节点来进行分割,然后在进行合并,和21. 合并两个有序链表,双指针判断大小合并,最终连接上剩余的节点。
image.png

  1. public ListNode sortList(ListNode head) {
  2. if (head == null && head.next == null) return head;
  3. // 前后指针找到中间节点
  4. ListNode slow = head, fast = head.next;
  5. while (fast.next != null && fast.next.next != null) {
  6. fast = fast.next.next;
  7. slow = slow.next;
  8. }
  9. // 递归将节点分开
  10. ListNode tmpNode = slow.next;
  11. // 注意设置为null,才是一个单链表
  12. slow.next = null;
  13. ListNode left = sortList(head);
  14. ListNode right = sortList(tmpNode);
  15. // 设置一个虚拟头节点,用来连接
  16. ListNode node = new ListNode(0);
  17. ListNode dummy = node;
  18. while (left != null && right != null) {
  19. if (left.val < right.val) {
  20. node.next = left;
  21. left = left.next;
  22. }else {
  23. node.next = right;
  24. right = right.next;
  25. }
  26. node = node.next;
  27. }
  28. // 合并还剩余的节点
  29. node.next = left == null ? right : left;
  30. // 返回头节点
  31. return dummy.next;
  32. }

迭代版本

迭代版本直接操作链表,设置一个虚拟头节点用来记录头节点,使用一个变量intv来表示每次合并的区间,例如:
image.png
直到intv==链表长度,表示合并完成。
image.png
模拟上述操作:
image.png

  1. // 迭代
  2. public ListNode sortList(ListNode head) {
  3. if (head == null || head.next == null) return head;
  4. // 使用虚拟头节点,来记录
  5. ListNode dummy = new ListNode(0);
  6. dummy.next = head;
  7. int len = getLength(head);
  8. int itrv = 1;
  9. // 合并的区间等于链表长度就不在合并
  10. while (itrv < len) {
  11. // 表示当前节点
  12. ListNode h = dummy.next;
  13. ListNode pre = dummy;
  14. while (h != null) {
  15. int i = itrv;
  16. ListNode h1 = h;
  17. for (; h != null && i > 0; i--) {
  18. h = h.next;
  19. }
  20. // 如果没有第二条链表,直接break
  21. if (i > 0) break;
  22. i = itrv;
  23. ListNode h2 = h;
  24. // 如果有,找到第二条链表
  25. for (; h != null && i > 0; i--) {
  26. h = h.next;
  27. }
  28. // 此时开始合并2个链表
  29. // 前部分链表的长度
  30. int left = itrv;
  31. // 后部分链表的长度,减i的情况就是如果后面链表长度不足itrv
  32. int right = itrv - i;
  33. while (left > 0 && right > 0) {
  34. if (h1.val < h2.val) {
  35. pre.next = h1;
  36. h1 = h1.next;
  37. left--;
  38. } else {
  39. pre.next = h2;
  40. h2 = h2.next;
  41. right--;
  42. }
  43. pre = pre.next;
  44. }
  45. // 连接剩余的
  46. pre.next = left > 0 ? h1 : h2;
  47. // 这对处理完,移动到最后,开始第二组,注意移动最长的
  48. // 第二段可以不等于itrv
  49. while (left > 0 || right > 0) {
  50. pre = pre.next;
  51. left--;
  52. right--;
  53. }
  54. // 第二段头节点赋值给第一段
  55. pre.next = h;
  56. }
  57. // 扩大两倍合并
  58. itrv *= 2;
  59. }
  60. return dummy.next;
  61. }
  62. public int getLength(ListNode head) {
  63. ListNode cur = head;
  64. int len = 0;
  65. while (cur != null) {
  66. cur = cur.next;
  67. len++;
  68. }
  69. return len;
  70. }

image.png