148. 排序链表

90 阅读1分钟

[148. 排序链表]

「这是我参与2022首次更文挑战的第27天,活动详情查看:2022首次更文挑战」。

题目描述

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

示例

示例1 :

image.png

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

示例 2:

image.png

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

示例 3:

输入: head = []
输出: []

提示:

  • 链表中节点的数目在范围 [0, 5 * 104] 内
  • -105 <= Node.val <= 105  

进阶:你可以在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序吗?

思路

我们已经做了好多道链表相关的题了,今天这里结合双指针,排序算法,递归来看一下这道题。由于链表只能单向访问并且不能随机读取,但是在拼接上十分方便,所以对链表的排序使用归并排序。归并排序是分而治之,递归的思想,其在链表思路是将链表分成两段排序好的链表,再将这两段链表合并起来,这两段排序好的链表也是通过上述思路,进行分割合并得到的,是一个递归的过程。具体的操作有分割和合并。

代码实现

按以上思路,分割需要实现找中间节点,我们之前已经写过。来看下递归的合并函数,递归函数的重要一点就是要明确退出的条件。合并链表时,使用一个哑节点,更方便我们的编码。


class Solution {
private:
    ListNode* middleNode(ListNode* head) {         //快慢指针找中间节点
        ListNode* slow = head;
        ListNode* fast = head;
        while (fast->next != nullptr && fast->next->next != nullptr) {
            slow = slow->next;
            fast = fast->next->next;
        }
        return slow;
    }
public:
    ListNode* sortList(ListNode* head) {
        if(head == NULL || head->next == NULL) //节点为空或单节点不需合并直接退出
            return head;
        /* 分割链表 */    
        ListNode* brk=middleNode(head);
        ListNode* mid=brk->next;
        brk->next = nullptr;
        
        ListNode* head1 = sortList(head);
        ListNode* head2 = sortList(mid);
        
        ListNode dummy(0);
        ListNode* cur = &dummy;
     
         while(head1 != nullptr &&  head2 != nullptr){  //合并链表
            if(head1->val >= head2->val){
                cur->next = head2;
                head2 = head2->next;
                
            }else {
                cur->next = head1;
                head1 = head1->next;
            }
                cur = cur->next;
        }
        cur->next=head1==nullptr?head2:head1;    //拼接余下部分
        return dummy.next;
    }
};


总结

在链表上使用双指针,使用归并排序在链表上进行排序。