合并升序链表系列(Java)

2023-11-15

LeetCode原题链接:

目录

合并两个有序链表:

题目表述: 

解法一: 

解法二:

合并K个升序链表

题目描述:

解法一:

解法二:


合并两个有序链表:

题目表述: 

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

样例:   

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

解法一: 

        对于这道题,我们可以创建一个新的头节点res,用于保存头节点,我们需要根据情况的不同进行不同的处理:

  • l1.val>l2,val,此时我们只将【l1.val】添加到res中,【l1】后移,【cur】后移
  • l2.val>l1.val,此时我们只将【l2.val】添加到res中,【l2】后移,【cur】后移
  • l1 == null && l2 == null,此时我们需要将两个节点都加进去(此处我们一起加两个是因为那个没被选到的值在下次比较一定会被加进去,所以索性一次把事情做完),但是要注意的是,我们在添加一个节点后,需要立马将【cur】后移,这样才能将节点正确添加到末尾(这里不懂想想尾插法)
  • l1 != null && l2 == null,此时我们只对【l1】处理即可,处理完后移cur指针
  • l1 != null && l2 == null,此时我们只对【l1】处理即可,处理完后移cur指针
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        ListNode res = new ListNode(0);
        ListNode cur = res;
        while(l1 != null || l2 != null){
            if(l1 !=null && l2 != null){
                //但两个节点相等,一起添加
                if(l1.val == l2.val){
                    cur.next = new ListNode(l1.val);
                    //在对链表进行添加元素后要立马将cur后移才能正确添加新节点
                    cur = cur.next;
                    cur.next = new ListNode(l2.val);
                    l1 = l1.next;
                    l2 = l2.next;
                }else{
                    if(l1.val>l2.val){
                        cur.next = new ListNode(l2.val);
                        l2 = l2.next;
                    }else{
                        cur.next = new ListNode(l1.val);
                        l1 = l1.next;
                    }
                }
            //到这说明li不为null,l2为null
            }else if(l1!=null){
                cur.next = new ListNode(l1.val);
                l1 = l1.next;
            //同上
            }else if(l2!=null){
                cur.next = new ListNode(l2.val);
                l2 = l2.next;
            }
            //讲主链表(res)的尾指针也就是cur往后面移动方便添加
            cur = cur.next;
        }
        //因为节点都是往res后面添加的,所以返回的是res.next,而不是res
        return res.next;
    }

效率:

解法二:

        基于对解法一的了解,我们有了解法二,这次不是对res采用添加节点的方法了,而是将两个链表连接起来。如下图示例(连接方式不唯一):

         有一定基础的同学应该发现了,我们只需要定好一个节点为头节点,然后逐个连接起来就可以了,所以这里我们需要三个节点指针【pre:表示结果列表的尾指针】、【cur1:表示一条链表的节点指针】、【cur2:表示一条链表的节点指针】,就拿我们这个图例举例说明:

        所以由图可以看出我们的连接工作就是只移动被选择的那个节点所在的链表即可(cur后移)当然pre同样也需要后移。

        在我们了解如何连接后,该如何确定pre是哪一条链表上的节点呢?cur1和cur2指向是如何进行确定的呢??我们通过代码来进行说明:

ListNode head = list1.val<=list2.val?list1:list2;
// cur1指向头,方便确认cur2的指向
ListNode cur1 = head;
// 判断节点为1,还是2,如果为1,就让cur2 指向2,如果为2,就指向1,
ListNode cur2 = head==list1? list2:list1;
ListNode pre = head;
cur1 = head.next;

         使用一个名为head的节点直接指向首节点最小的那个链表,这时就可以知道【cur2】的节点指向了,使用head==list1? list2:list1来判断,假如head是list2,那么【cur2】指向list1,否则指向list2,也很好理解,它的指向肯定是head的下一位,【pre】开始就是head这个也好理解。那么接下里就是我们的处理过程了

while(cur1 != null && cur2 != null){
    if(cur1.val<=cur2.val){
        pre.next = cur1;
        cur1 = cur1.next;
    }else if(cur1.val > cur2.val){
        pre.next = cur2;
        cur2 = cur2.next;
    }
    pre = pre.next;
}
pre.next = cur1 != null? cur1 : cur2!= null?cur2:null;

        一目了然,就单纯的链表拼接流程,一个拼接两个后移,最后判断哪个链表没有被遍历完,即将其添加到【pre】屁股后面。

注意,我们的循环条件是【&&】,不是解法一中的【 || 】

完整代码:

    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        if(list1 == null || list2 == null){
            return list1 == null?list2:list1;
        }
        ListNode head = list1.val<=list2.val?list1:list2;
        // cur1指向头,方便确认cur2的指向
        ListNode cur1 = head;
        // 判断节点为1,还是2,如果为1,就让cur2 指向2,如果为2,就指向1,
        ListNode cur2 = head==list1? list2:list1;
        ListNode pre = head;
        cur1 = head.next;
        while(cur1 != null && cur2 != null){
            if(cur1.val<=cur2.val){
                pre.next = cur1;
                cur1 = cur1.next;
            }else if(cur1.val > cur2.val){
                pre.next = cur2;
                cur2 = cur2.next;
            }
            pre = pre.next;
        }
        pre.next = cur1 != null? cur1 : cur2!= null?cur2:null;
        return head;
    }

 效率:

合并K个升序链表

题目描述:

        给你一个链表数组,每个链表都已经按升序排列。请你将所有链表合并到一个升序链表中,返回合并后的链表。

输入: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

在了解了上面的合并链表后,我们也可以基于这两个不同的解法,实现多链表的合并

解法一:

        在【合并两个升序链表】中,我们需要知道谁是最小就能做到添加节点的操作,但是在多链表中,我们该如何之后多链表的首节点谁最小呢?暴力枚举,简单粗暴,对链表数组进行遍历,记录最小值

for(int j = 0; j < n; j++){
    if(lists[j] != null){
        //获取最小值
        min = Math.min(min,lists[j].val);
     }
}

        在我们获取到最小值后,再次对链表数组进行遍历,只要这些链表的首元素等于最小值,我们就将其节点添加到【res:答案链表首节点】中,如何将这些符合规则的链表后移,其他的保存不变,这里需要注意的是,我们需要跳过链表值为null的节点,因为它已经被遍历完了,所以我们不需要对他进行遍历了。

//找符合条件的节点
for(int j = 0; j < n; j++){
    if(lists[j] != null){
        int num = lists[j].val; 
        if(num == min){
            //创建节点
            cur.next = new ListNode(num);
            //指针后移
            cur = cur.next;
            //节点后移
            lists[j] = lists[j].next;
        }
    }
}

        最后我们要处理最后一个问题,【对链表数组遍历多少次,才能将每个节点都全部加入到res链表中???】

测试用例范围是:

  • k == lists.length
  • 0 <= k <= 10^4
  • 0 <= lists[i].length <= 500

对于最坏情况,一共由500*10**4个节点,也就是处理n*list.length个节点,所以我们需要对这个数据进行精准计算,所以需要循环遍历一遍原始的数组链表

int counter = 0;
for(ListNode list: lists){
    ListNode temp = list;
    while(temp!=null){
        temp = temp.next;
        counter++;
    }
}

        其实这个counter是当前情况的最坏遍历次数,因为之中,可能存在多个与min相等的节点存在,这样就导致由有些节点已经处理过了,这是这个解法的一个可以优化的地方,但是这里我就不做优化处理了,直接放完整代码

public ListNode mergeKLists(ListNode[] lists) {
    ListNode res = new ListNode(0);
    ListNode cur = res;
    int n = lists.length;
    //循环节点个数,每次取第一个节点的值,来进行判断谁加入节点,假如节点等于min,就将节点加入到链表中,然后向后移动一位
    int counter = 0;
    for(ListNode list: lists){
        ListNode temp = list;
        while(temp!=null){
            temp = temp.next;
            counter++;
        }
    }
    for(int i = 0; i < counter; i++){
        int min = Integer.MAX_VALUE;
        for(int j = 0; j < n; j++){
            if(lists[j] != null){
                //获取最小值
                min = Math.min(min,lists[j].val);
            }
        }
        //找符合条件的节点
        for(int j = 0; j < n; j++){
            if(lists[j] != null){
                int num = lists[j].val; 
                if(num == min){
                    //创建节点
                    cur.next = new ListNode(num);
                    //指针后移
                    cur = cur.next;
                    //节点后移
                    lists[j] = lists[j].next;
                }
            }
        }
    }
    return res.next;
}

效率:(确实慢了)

解法二:

        解法二是基于【合并两个排序链表】的解法二实现的:其思想很简单,四个字,化繁为简,既然你是多个链表,我就取两条出来单独处理,这两条组成的新链表,然后重新取一条链表进行合并处理,这样就把多链表问题又化为了双链表问题。直接放代码(合并操作一模一样,只是需要遍历数组链表来不断调用合并函数)

// 基本思路和合并两个链表的一样,因为是多个链表,所以我们化繁为简
// 两条链表合并为一条新链表然后和另一条链表进行合并操作
public ListNode mergeKLists(ListNode[] lists) {
    ListNode ans = null;
    for(ListNode list: lists){
        ans = mergeTwoLists(ans,list);
    }
    return ans;
}
//实现合并函数
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
    if(list1 == null || list2 == null){
        return list1 == null?list2:list1;
    }
    ListNode head = list1.val<=list2.val?list1:list2;
    // cur1指向头,方便确认cur2的指向
    ListNode cur1 = head;
    // 判断节点为1,还是2,如果为1,就让cur2 指向2,如果为2,就指向1,
    ListNode cur2 = head==list1? list2:list1;
    ListNode pre = head;
    cur1 = head.next;
    while(cur1 != null && cur2 != null){
        if(cur1.val<=cur2.val){
            pre.next = cur1;
            cur1 = cur1.next;
        }else if(cur1.val > cur2.val){
            pre.next = cur2;
            cur2 = cur2.next;
        }
        pre = pre.next;
    }
    pre.next = cur1 != null? cur1 : cur2!= null?cur2:null;
    return head;
}

 感谢阅读owo

声明:本文章仅作为学习笔记使用

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

合并升序链表系列(Java) 的相关文章

随机推荐

  • linux module 目录,/sys/module/ 模块信息目录与/proc/modules文件

    在内核模块编译中 会选择编译成模块 或者build in 内核镜像中 其中对内核模块有很好的的说明 这也是linux在嵌入式当中得到广泛应用的充分体现 内核中有很多功能选项 其中有许多使我们不需要的 内核设计成模块的优势所在就在这里 不需要
  • [Python从零到壹] 三十七.图像处理基础篇之图像融合处理和ROI区域绘制

    欢迎大家来到 Python从零到壹 在这里我将分享约200篇Python系列文章 带大家一起去学习和玩耍 看看Python这个有趣的世界 所有文章都将结合案例 代码和作者的经验讲解 真心想把自己近十年的编程经验分享给大家 希望对您有所帮助
  • 常见七大排序算法

    目录 前言 冒泡排序 选择排序 插入排序 希尔排序 shell 快速排序 归并排序 计数排序 前言 在前面我发布了常见的七大排序算法的相关博客 今天这一篇文章是做一个排序算法的小总结 把前面的博客集中分类到一起 方便大家查看 下面就可以去通
  • 树莓派 /bin/sh: 1: /usr/bin/apt-listchanges: not found 返回了一个错误号 (1) --apt

    问题 bin sh 1 usr bin apt listchanges not found E 子进程 usr bin apt listchanges apt test lt 10 返回了一个错误号 1 E Failure running
  • 分布式环境下使用RSA算法实现登录密码的加密传输

    目录 效果 RSA介绍 实现思路 服务端实现 RSAService RSA算法的相关操作 RedisService 公钥和密钥的存储和获取 获取公钥的接口 客户端使用公钥加密 服务端使用私钥解密 效果 RSA介绍 RSA是一种非对称加密算法
  • 免费获取JetBrains一年全家桶

    原文链接 https blog csdn net li5672 article details 110231645 登录Github Education 点击Get benefits 点击Get student benefits 下一步以后
  • CHL同步队列是什么

    CHL同步队列就是AQS内部维护的一个FIFO双向队列 AQS依赖这个双向队列来完成同步状态的管理 如果当前线程获取同步状态失败 AQS将会将当前线程以及等待状态信息构建成一个节点 Node 并将其加入到同步队列中 同时会阻塞当前线程 当同
  • linux 创建 svn 库

    cd data svn mkdir p itvalue chown R windmaker windmaker itvalue svnadmin create data svn itvalue cd itvalue cd conf vim
  • android studio中的CMakeLists.txt,就是如此简单

    android studio中的CMakeLists txt 就是如此简单 user Linvest 目录 1 cmake minimum required VERSION 3 4 1 2 add library native lib SH
  • RabbitMQ简介与安装

    技术对比 MQ 中文是消息队列 MessageQueue 字面来看就是存放消息的队列 也就是事件驱动架构中的Broker 比较常见的MQ实现 ActiveMQ RabbitMQ RocketMQ Kafka 几种常见MQ的对比 Rabbit
  • Android的ADB工具使用

    在SDK的Tools文件夹下包含着Android模拟器操作的重要命令ADB ADB的全称为Android Debug Bridge 就是调试桥的作用 借助这个工具 我们可以管理设备或手机模拟器的状态 还可以进行以下的操作 1 快速更新设备或
  • react-antd中表格的使用(数据的请求,带删除功能的表格)

    前言 最近在学习react antd框架 表格这一块在项目中的使用频率很高 最近在学习这一块的内容 所以记录一下 基础表格请求数据 一般对于表格中的数据我们会进行请求 将请求到的数据存入表格中展示出来 当我们请求较少时可以这样写 const
  • AssetDatabase的方法

    静态函数 描述 AddObjectToAsset 添加对象到资产 AllowAutoRefresh 递减一个内部计数器 Unity使用它来决定是否允许自动的资产数据库刷新行为 AssetPathToGUID 获得资产的GUID ClearL
  • Unity 3D 资源下载

    Unity 3D 资源下载 你也可以在 Unity 3D 中执行 Window Asset Store 菜单命令直接访问 Unity 资源商店 Asset Store Unity 资源商店简介 Unity 资源商店https www ass
  • 为curl 、git、go语言、wget、repo设置代理,解决ubuntu 18.04编译chromium os问题

    为CURL设置proxy 设置代理的方式搜索了一下挺多的 我测试了这两种方式 这两种方式在ubuntu 18 04上可以运行 有两种方法 第一通过声明环境变量 export http proxy socks5h 127 0 0 1 1080
  • a 标签 onclick ( not a function)

    文章目录 说明 点击 download 点击 download1 说明 如图 download 在 body 下方声明 点击 download 则报错 download is not a function 点击 download1 则可以正
  • 【LeetCode刷题】

    菜鸡的LeetCode打怪记录 tips 本文涉及的一切内容仅本人学习使用 如不慎发生侵权行为 请滴滴我删除 谢谢 文章目录 菜鸡的LeetCode打怪记录 题目1480 Running Sum of 1d Array 思路 代码 评分结果
  • 合法ip算法实现——输入字符串,输出所有合法的ip

    输入为一串0 9之间的数字字符 不改变字符串中字符的前后顺序 输出所有合法的ip地址 IPV4下用一个32位无符号整数来表示一个ip地址 一般用点分方式来显示 点将ip地址分成4个部分 如 10 137 17 1 所以要输出所有合法ip 1
  • 2020暑假实习-百度前端一面&二面&三面

    2020暑假实习 百度前端一面 二面 三面 一面 算法题 JS实现二分搜索 随机打乱数组 HTML cookie localStorage sessionStorage区别 标签页之间的通信 cookie setInterval local
  • 合并升序链表系列(Java)

    LeetCode原题链接 21 合并两个有序链表 力扣 LeetCode 23 合并K个升序链表 力扣 LeetCode 目录 合并两个有序链表 题目表述 解法一 解法二 合并K个升序链表 题目描述 解法一 解法二 合并两个有序链表 题目表