排序与链表 力扣[21. 合并两个有序链表,23. 合并K个升序链表,148. 排序链表]

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 

示例 1:

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

示例 2:

输入:l1 = [], l2 = []
输出:[]

示例 3:

输入:l1 = [], l2 = [0]
输出:[0]

提示:

  • 两个链表的节点数目范围是 [0, 50]
  • -100 <= Node.val <= 100
  • l1 和 l2 均按 非递减顺序 排列

思路:

循环对比两个链表当前值, 每次取较小的加入结果链表,循环后需要判断两个链表中是否存在一个没有遍历完,将剩余的链表加入结果链表,实现上可通过哑节点降低复杂度。

查看代码
/**
 * 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) {
        ListNode* dummy=new ListNode();
        ListNode* cur=dummy;
        while(list1&&list2){
            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;
        }
        else if(list2!=nullptr){
            cur->next=list2;
        }
        return dummy->next;
    }
};

给你一个链表数组,每个链表都已经按升序排列。

请你将所有链表合并到一个升序链表中,返回合并后的链表。

示例 1:

输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
解释:链表数组如下:
[
  1->4->5,
  1->3->4,
  2->6
]
将它们合并到一个有序链表中得到。
1->1->2->3->4->4->5->6

示例 2:

输入:lists = []
输出:[]

示例 3:

输入:lists = [[]]
输出:[]

提示:

  • k == lists.length
  • 0 <= k <= 10^4
  • 0 <= lists[i].length <= 500
  • -10^4 <= lists[i][j] <= 10^4
  • lists[i] 按 升序 排列
  • lists[i].length 的总和不超过 10^4

思路:

上一个的题合并两个链表的代码可以直接复制过来调用

法一:逐一合并,比如先合并list[0]+list[1]=>list[1],接下来list[1]+list[2]=>list[2]...

共合并K-1次,且每次合并得到的链表会越来越长,与下一个链表合并时明显对比次数会很大。如第n次合并得到的list[n]和下一个链表list[n],长度很不均横。

复杂度分析来自官方题解

法二:

分治合并

示意图如下:

每轮两两合并,每轮合并后,下一轮需要合并的数量折半。

在实现上,设置步长(代表两个下标的距离) 每轮翻倍,interval依次等于1,2,4...,每次合并list[idx]和list[idx+internal]。即list[0]list[0+1]合并,下一轮list[0]list[0+2]合并...

idx通过每次增加两倍步长更新。

查看代码
/**
 * 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) {
        ListNode* dummy=new ListNode();
        ListNode* cur=dummy;
        while(list1&&list2){
            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;
        }
        else if(list2!=nullptr){
            cur->next=list2;
        }
        return dummy->next;
    }
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        int len=lists.size();
        if(len==0)
            return nullptr;
        //每次两两合并
        int interval=1;//步长
        while(interval<len){//步长小于总长
            for(int idx=0;idx<len;idx+=interval*2)//下标每次+2*步长
            {
                if(idx+interval<len){//当前下标加步长不能越界
                    //合并下标idx和idx+interval
                    lists[idx]=mergeTwoLists(lists[idx],lists[idx+interval]);
                }
            }
            interval*=2;//步长每次*2
        }
        return lists[0];//合并完之后list[0]为结果
    }
};

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

示例 1:

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

示例 2:

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

示例 3:

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

提示:

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

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

思路:

和上一题类似,采用分治合并,每轮两两合并,随着轮数增加需要合并的链表数量会折半,并且长度均衡增加。

调用第一题的合并函数,合并时自动排序。在实现上要注意指针移动的细节。

head = [4,2,1,3]
输出:[1,2,3,4]
4  -> 2    1 -> 3    
|_____|    |____|  每轮两两合并
   |         |
  2->4     1->3
   |________|
        |
      1->2->3->4
    
查看代码
 /**
 * 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* dummy=new ListNode(0,head);
        for(int interval=1;interval<length;interval*=2){//每轮步长翻倍
            ListNode* pre=dummy;
            ListNode* cur=dummy->next;
            while(cur){//每轮中两两合并排序
                //每次排序两个链表,先找到第一个链表
                ListNode* head1=cur;
                for(int i=1;i<interval&&cur->next;i++){
                    cur=cur->next;
                }
                //第一个链表找完了,cur下一个节点为第二个链表头,再从cur断开,将第一个链表分离出来
                ListNode* head2=cur->next;
                cur->next=nullptr;//断开
                cur=head2;//恢复cur
                //找第二个
                for(int i=1;i<interval&&cur&&cur->next;i++){
                    cur=cur->next;
                }
                ListNode* next=nullptr;//记录cur的下一个节点,再从cur断开,将第二个链表分离出来
                if(cur){
                    next=cur->next;
                    cur->next=nullptr;
                }
                ListNode* merged=mergeTwoLists(head1,head2);//合并
                pre->next=merged;//cur更新为合并后的链表
                //pre也要更新
                while(pre->next){
                    pre=pre->next;
                }
                cur=next;//利用next恢复cur
            }
        }
        return dummy->next;
    }
    //之前的函数
    ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
        ListNode* dummy=new ListNode();
        ListNode* cur=dummy;
        while(list1&&list2){
            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;
        }
        else if(list2!=nullptr){
            cur->next=list2;
        }
        return dummy->next;
    }
};

 

 
posted @ 2023-02-06 10:14  付玬熙  阅读(19)  评论(0编辑  收藏  举报