首页 > 其他分享 >排序与链表 力扣[21. 合并两个有序链表,23. 合并K个升序链表,148. 排序链表]

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

时间:2023-02-06 10:22:05浏览次数:69  
标签:list2 ListNode cur list1 next 链表 升序 排序

21. 合并两个有序链表

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

示例 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;
    }
};

23. 合并K个升序链表

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

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

示例 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]为结果
    }
};

148. 排序链表

给你链表的头结点 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;
    }
};

 

 

标签:list2,ListNode,cur,list1,next,链表,升序,排序
From: https://www.cnblogs.com/fudanxi/p/17078562.html

相关文章

  • 1.2 链表
    和数组不同的是,链表并不需要一块连续的内存空间,它可以通过指针将一组零散的内存块串联起来使用。链表的基本操作是最考验逻辑思维能力的,尤其需要注意:指针/引用的含义哨......
  • 部分排序
    给定一个整数数组,编写一个函数,找出索引m和n,只要将索引区间[m,n]的元素排好序,整个数组就是有序的。注意:n-m尽量最小,也就是说,找出符合条件的最短序列。函数返回值为[m,n],若不......
  • drf之分页,过滤,排序
    #4.过滤Filtering对于列表数据可能需要根据字段进行过滤,我们可以通过添加django-fitlter扩展来增强支持。```pipinstalldjango-filter```settings.py,代码:```p......
  • 算法导论:堆排序
    维护堆主要思想比较\(A[i],A[Left(i)]\)和\(A[Right(i)]\)的大小如果\(A[i]\)不是最大的,则将其与比较大的孩子节点进行交换在堆中继续向下比较和交换,直到\(i......
  • 代码随想录算法训练营Day5 数组、链表复习
    数组部分数组最重要的思维方式是双指针的使用。快慢指针在进行元素移除和元素操作时会使用两个for循环嵌套,此时时间复杂度为O(n²)。在for循环中通过双指针(快慢指针)的使......
  • python 排序的几种方式
     #python排序的方法#Python列表有一个内置的list.sort()方法可以直接修改列表list1=[1,3,5,10,2,1]list1.sort()print(list1)list1=[1,3,5,10,2,1]list......
  • 删除链表的倒数第N个节点
    链接:删除链表的倒数第N个节点输入:head=[1,2,3,4,5],n=2输出:[1,2,3,5]题解一一个直观的想法是一次遍历计算链表长度,之后对于给定的n,可以很快找到欲删除的结点。......
  • 希尔排序
    #include<iostream>usingnamespacestd;/***希尔排序*希尔排序是插入排序的的改进*改进的主要思想是:*1.插入排序对于小规模数组效率较高*......
  • 俩猴排序
    众所周知,猴子排序的原理是打乱序列,然后判断是否有序。可以看到,猴子排序的时间复杂度为\(\Omega(n),O(?)\)。通常打乱序列的方式为:for(inti=0;i<n;++i){ swap......
  • 4.6链表使元素的追加和删除更容易
    在数组的各个元素中,除了数据的值之外,通过为其附带上下一个元素的索引,即可实现链表。数据的值和下一个元素的索引组合在一起,就构成了数组的一个元素。这样,数组元素相连就构......