Leetcode.0148 | 排序链表

题目

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

要求时间复杂度为O(nlogn)

示例

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

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

解决方法

使用归并排序。步骤如下:

1. 找到链表的中点,以中点为分界,将链表拆分成两个子链表。寻找链表的中点可以使用快慢指针的做法,快指针每次移动2步,慢指针每次移动1步,当快指针到达链表末尾时,慢指针指向的链表节点即为链表的中点。

2. 对两个子链表分别排序。

3. 合并两个子链表的结果。将两个升序链表合并为一个新的升序链表并返回,合并链表部分的代码实现如下所示:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
        if(list1 == nullptr && list2 == nullptr) return nullptr;
        if(list1 != nullptr && list2 == nullptr) return list1;
        if(list1 == nullptr && list2 != nullptr) return list2;
        ListNode* head = new ListNode(0);
        if(list1->val > list2->val){
            head->next = list2;
            list2 = list2->next;            
        }else{
            head->next = list1;
            list1 = list1->next;
        }
        ListNode* cur = head->next;
        while(list1 != nullptr && list2 != nullptr){
            if(list1->val > list2->val){
                cur->next = list2;
                list2 = list2->next;
            }else{
                cur->next = list1;
                list1 = list1->next;
            }
            cur = cur->next;
        }
        if(list1 != nullptr){
            cur->next = list1;
        }
        if(list2 != nullptr){
            cur->next = list2;
        }
        return head->next;
    }
};

代码实现

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* sortList(ListNode* head) {
       ListNode* res = sort(head,nullptr);
       return res;
    }
    ListNode* sort(ListNode* head,ListNode* tail){
        if (head == nullptr) {
            return head;
        }
        if (head->next == tail) {
            head->next = nullptr;
            return head;
        }
        ListNode* slow = head, *fast = head;
        while(fast != tail && fast->next != tail){
            fast = fast->next->next;
            slow = slow->next;
        }
        ListNode* mid = slow;
        ListNode* list1 = sort(head, mid);
        ListNode* list2 = sort(mid, tail);
        ListNode* res = merge(list1, list2);
        return res;
    }
    ListNode* merge(ListNode* list1, ListNode* list2) {
        if(list1 == nullptr && list2 == nullptr) return nullptr;
        if(list1 != nullptr && list2 == nullptr) return list1;
        if(list1 == nullptr && list2 != nullptr) return list2;
        ListNode* head = new ListNode(0);
        if(list1->val > list2->val){
            head->next = list2;
            list2 = list2->next;            
        }else{
            head->next = list1;
            list1 = list1->next;
        }
        ListNode* cur = head->next;
        while(list1 != nullptr && list2 != nullptr){
            if(list1->val > list2->val){
                cur->next = list2;
                list2 = list2->next;
            }else{
                cur->next = list1;
                list1 = list1->next;
            }
            cur = cur->next;
        }
        if(list1 != nullptr){
            cur->next = list1;
        }
        if(list2 != nullptr){
            cur->next = list2;
        }
        return head->next;
    }
};

进阶:非递归解法

采用递归解法时,需要开栈,空间复杂度为O(logn).当需要常数级的空间复杂度时,需要将递归解法转化为非递归解法。在这种情况下,需要首先求出链表的长度,然后再将大链表拆分成子链表进行合并。具体步骤如下所示:

1. 用subLength 表示每次需要排序的子链表的长度,初始时 subLength=1。

2. 每次将链表拆分成若干个长度为 subLength 的子链表(最后一个子链表的长度可以小于 subLength),按照每两个子链表一组进行合并,合并后即可得到若干个长度为 subLength×2 的有序子链表(最后一个子链表的长度可以小于 subLength×2)。

3. 将 subLength 的值乘2,重复第 2 步,对更长的有序子链表进行合并操作,直到有序子链表的长度大于或等于 length,整个链表排序完毕。

更详细的说明见代码注释:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* sortList(ListNode* head) {
       if (head == nullptr) {
            return head;
        }
        int length = 0;
        ListNode* node = head;
        while (node != nullptr) {
            length++;
            node = node->next;
        }
        ListNode* dummyHead = new ListNode(0, head);
        //每次将链表拆分成若干个长度为 subLength 的子链表,初始值为1
        for (int subLength = 1; subLength < length; subLength = subLength*2) {
            ListNode* prev = dummyHead, *curr = dummyHead->next;
            while (curr != nullptr) {  //对整个大链表两个两个进行合并,每个循环代表合并两个子链表。
                ListNode* head1 = curr;
                //找到第一个子链表的末尾
                for (int i = 1; i < subLength && curr->next != nullptr; i++) {
                    curr = curr->next;
                }
                //将第一个子链表的尾巴断开
                ListNode* head2 = curr->next;
                curr->next = nullptr;
                curr = head2;
                //找到第二个子链表的末尾
                for (int i = 1; i < subLength && curr != nullptr && curr->next != nullptr; i++) {
                    curr = curr->next;
                }
                //将第二个子链表的尾巴断开
                ListNode* next = nullptr;
                if (curr != nullptr) {
                    next = curr->next; //保存断开位置的下一个节点。即这两个子链表合并结束后,下两个子链表的开头。
                    curr->next = nullptr;
                }
                //合并第一个第二个子链表
                ListNode* merged = merge(head1, head2);
                prev->next = merged;
                while (prev->next != nullptr) {
                    prev = prev->next;
                }
                curr = next;  //开始对后面的子链表进行合并
            }
        }
        return dummyHead->next;
    }
    ListNode* merge(ListNode* list1, ListNode* list2) {
        if(list1 == nullptr && list2 == nullptr) return nullptr;
        if(list1 != nullptr && list2 == nullptr) return list1;
        if(list1 == nullptr && list2 != nullptr) return list2;
        ListNode* head = new ListNode(0);
        if(list1->val > list2->val){
            head->next = list2;
            list2 = list2->next;            
        }else{
            head->next = list1;
            list1 = list1->next;
        }
        ListNode* cur = head->next;
        while(list1 != nullptr && list2 != nullptr){
            if(list1->val > list2->val){
                cur->next = list2;
                list2 = list2->next;
            }else{
                cur->next = list1;
                list1 = list1->next;
            }
            cur = cur->next;
        }
        if(list1 != nullptr){
            cur->next = list1;
        }
        if(list2 != nullptr){
            cur->next = list2;
        }
        return head->next;
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值