leetcode:148. 排序链表

题目

给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表

示例 1:
[中等] 148. 排序链表 - 图1

  1. 输入:head = [4,2,1,3]
  2. 输出:[1,2,3,4]

示例 2:
[中等] 148. 排序链表 - 图2

  1. 输入:head = [-1,5,3,4,0]
  2. 输出:[-1,0,3,4,5]

示例 3:

  1. 输入:head = []
  2. 输出:[]

解答 & 代码

解法一:自顶向下归并排序(递归)

  1. 定位到链表中间节点 mid,将链表拆分成左、右两个子链表
  2. 分别递归排序左子链表、右子链表
  3. 将排序后的左、右子链表合并

    1. /**
    2. * Definition for singly-linked list.
    3. * struct ListNode {
    4. * int val;
    5. * ListNode *next;
    6. * ListNode() : val(0), next(nullptr) {}
    7. * ListNode(int x) : val(x), next(nullptr) {}
    8. * ListNode(int x, ListNode *next) : val(x), next(next) {}
    9. * };
    10. */
    11. class Solution {
    12. private:
    13. // 寻找链表中间节点并返回
    14. ListNode* findMidNode(ListNode* head)
    15. {
    16. // 注意这里快、慢指针都从虚拟头节点开始遍历,这样偶数节点链表定位到中间节点,奇数节点
    17. // 链表则定位到第一个中间节点,这样得到的中间节点就可以作为左子节点的最后一个节点。
    18. // 而如果从 head 开始遍历,奇数节点链表会定位到第二个中间节点,就只能得到右子链表的起点
    19. ListNode* dummyHead = new ListNode(0, head);
    20. ListNode* fast = dummyHead;
    21. ListNode* slow = dummyHead;
    22. while(fast != nullptr && fast->next != nullptr)
    23. {
    24. fast = fast->next->next;
    25. slow = slow->next;
    26. }
    27. return slow;
    28. }
    29. // 合并两个有序链表
    30. ListNode* mergeList(ListNode* head1, ListNode* head2)
    31. {
    32. ListNode* dummyHead = new ListNode();
    33. ListNode* pre = dummyHead;
    34. ListNode* cur1 = head1;
    35. ListNode* cur2 = head2;
    36. while(cur1 != nullptr && cur2 != nullptr)
    37. {
    38. if(cur1->val < cur2->val)
    39. {
    40. pre->next = cur1;
    41. pre = cur1;
    42. cur1 = cur1->next;
    43. }
    44. else
    45. {
    46. pre->next = cur2;
    47. pre = cur2;
    48. cur2 = cur2->next;
    49. }
    50. }
    51. if(cur1 != nullptr)
    52. pre->next = cur1;
    53. if(cur2 != nullptr)
    54. pre->next = cur2;
    55. return dummyHead->next;
    56. }
    57. public:
    58. // 递归归并排序
    59. ListNode* sortList(ListNode* head) {
    60. // 递归结束条件:链表为空 or 只有一个节点
    61. if(head == nullptr || head->next == nullptr)
    62. return head;
    63. // 1. 定位到链表中间节点 mid,将链表拆分成左、右两个子链表
    64. ListNode* mid = findMidNode(head);
    65. ListNode* rightHead = mid->next; // 右子链表的头节点
    66. mid->next = nullptr; // 将左子链表的尾部指向 nullptr
    67. // 2. 分别递归排序左子链表、右子链表
    68. head = sortList(head);
    69. rightHead = sortList(rightHead);
    70. // 3. 将排序后的左、右子链表合并
    71. head = mergeList(head, rightHead);
    72. return head;
    73. }
    74. };

    复杂度分析:设链表长为 N

  • 时间复杂度 O(N logN):归并排序时间复杂度
  • 空间复杂度 O(log N):递归栈空间复杂度

执行结果:

  1. 执行结果:通过
  2. 执行用时:132 ms, 在所有 C++ 提交中击败了 12.01% 的用户
  3. 内存消耗:59.3 MB, 在所有 C++ 提交中击败了 5.02% 的用户

解法二:自底向上归并排序

[中等] 148. 排序链表
复杂度分析:设链表长为 N

  • 时间复杂度 O(N logN):
  • 空间复杂度 O(1):