【算法】【优选算法】链表

目录

  • 一、链表常用技巧与操作总结
  • 二、2.两数相加
  • 三、24.两两交换链表中的节点
    • 3.1 迭代
    • 3.2 递归
  • 四、143.重排链表
  • 五、23.合并K个升序链表
    • 5.1 堆
    • 5.2 分治
    • 5.3 暴力枚举
  • 六、25.K个⼀组翻转链表

一、链表常用技巧与操作总结

技巧:

  • 画图解题。
  • 使用虚拟头结点。
  • 像有插入类似操作时,直接定义变量,不用考虑节点丢失情况。
  • 快慢指针。

操作:

  • 创建新节点。
  • 尾插。
  • 头插。

二、2.两数相加

题目链接:2.两数相加
题目描述:

题目解析:

  • 给你两个链表,每个链表都表示一个逆位的数,将两个数相加。
  • 相加后的结果不会出现,链表最后节点为0的情况。

解题思路:

  • 模拟两个数字相加即可。
  • 遍历两个链表,对应节点值相加,放在一个变量sum中,结果链表就接上sum%10的节点,sum / 10表示进位的数。

解题代码:

//时间复杂度:O(n)
//空间复杂度:O(1)
class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        ListNode newHead = new ListNode(0);//虚拟头结点
        ListNode pre = newHead;
        int sum = 0;
        while(l1 != null || l2 != null || sum != 0) {
            if(l1 != null) {
                sum += l1.val;
                l1 = l1.next;
            }
            if(l2 != null) {
                sum += l2.val;
                l2 = l2.next;
            }
            pre.next = new ListNode(sum % 10);
            pre = pre.next;
            sum /= 10;
        }
        return newHead.next;
    }
}

三、24.两两交换链表中的节点

题目链接:24.两两交换链表中的节点
题目描述:

题目解析:

  • 就交换两个相邻节点即可。

3.1 迭代

解题思路:

  • 直接就交换节点即可,为避免节点丢失情况,我们就将交换过程涉及到的节点定义出来即可,然后根据交换后节点直接一一连接即可。
  • 交换之后移动这四个变量即可,循环交换就可以了。
  • 循环结束条件:当偶数节点个数的时候cur1为null时就该结束了。当奇数节点个数的时候cur2为null时结束。
  • 细节处理:当我们第一次定义变量的时候,为防止空指针异常,要将链表为空和链表为1个节点情况排除。

解题代码:

//时间复杂度:O(n)
//空间复杂度:O(1)
class Solution {
    public ListNode swapPairs(ListNode head) {
        if(head == null || head.next == null) return head;
        ListNode newHead = new ListNode(0);
        newHead.next = head;
        ListNode pre = newHead;
        ListNode cur1 = pre.next;
        ListNode cur2 = cur1.next;
        ListNode last = cur2.next;
        while(cur1 != null && cur2 != null) {
            //交换
            pre.next = cur2;
            cur2.next = cur1;
            cur1.next = last;

            //向后移动
            pre = cur1;
            if(pre.next == null) break;
            cur1 = pre.next;
            if(cur1.next == null) break;
            cur2 = cur1.next;
            
            last = cur2.next;
        }
        return newHead.next;
    }
}

3.2 递归

解题思路

  • 我们就交换两个节点,然后剩余节点又是一个待交换节点的链表。
  • 结束条件:链表为空或链表为1个节点。

解题代码:

//时间复杂度:O(n)
//空间复杂度:O(1)
class Solution {
    public ListNode swapPairs(ListNode head) {
        if(head == null || head.next == null) return head;
        
        ListNode newHead = head.next;
        head.next = newHead.next;
        newHead.next = head;
        head.next = swapPairs(head.next);
        
        return newHead;

    }
}

四、143.重排链表

题目链接:143.重排链表
题目描述:

题目分析:

  • 就是将链表按照题目给的规则重新排序。

解题思路:

  • 这道题其实就可以看成下面的3个步骤:
  • 先找到中间节点,再将后半部分链表逆置,再将两个子链表分别取一个节点的连接在一起。
  • 找到中间节点:使用快慢双指针。
  • 后半部分链表逆置:逆置采用头插法逆置,但是从哪里开始逆置有说法:像下图由于节点数奇偶性不同,slow走到位置不同(可能是正中间节点,也可能是下一个节点)。我们有两个逆置方案
    • 方案一:将slow以及其后节点都逆置。
    • 方案二:将slow后的节点逆置。因为本来slow和slow前一个节点在结果链表中的位置是不会变的。
  • 因为方便处理两个链表不循环,要让两个子链表末尾都指向null,方案一还需要去记录slow前一个节点,所以采取方案二。
  • 将两个子链表合并:直接连接即可。由于采取方案二,所以子链表1一定更长,使用这个当循环条件。
  • 细节问题:当链表有1个和2个节点时,不需要交换,并且会导致空指针异常,单独处理。

解题代码:

//时间复杂度:O(n)
//空间复杂度:O(1)
class Solution {
    public void reorderList(ListNode head) {
        if(head.next == null || head.next.next == null) return ;
        //快慢双指针 找到中间节点
        ListNode slow = head;
        ListNode fast = head;
        while(fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
        }

        //头插法 逆置
        ListNode head2 = new ListNode(0);
        ListNode cur = slow.next;
        slow.next = null;
        while(cur != null) {
            ListNode next = cur.next;
            cur.next = head2.next;
            head2.next = cur;
            cur = next;
        }
        head2= head2.next;
        //合并两个链表
        ListNode newHead = new ListNode(0);
        ListNode pre = newHead;
        while(head != null) {
            if(head != null) {
                pre.next = head;
                head = head.next;
                pre = pre.next;
            }
            if(head2 != null) {
                pre.next = head2;
                head2 = head2.next;
                pre = pre.next;
            }
        }
        head = newHead.next;
    }
}

五、23.合并K个升序链表

题目链接:23.合并K个升序链表
题目描述:

题目解析:

  • 给一个链表数组,将元素合并为一个链表,按照升序排列。

5.1 堆

解题思路:

  • 我们只需要使用一个小根堆来存储每次需要比较大小的节点,然后将最小的节点尾插放进结果链表。在将最小节点的后一个节点放入堆中即可。
  • 入堆的时候,需要判断节点是不是为空,空节点不能进入堆。
  • 当堆变空时,就完成合并了。

解题代码:

//时间复杂度:O(N*K*logK)
//空间复杂度:O(k)
class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        PriorityQueue<ListNode> heap = new PriorityQueue<>((l1,l2) -> l1.val-l2.val);
        ListNode ret = new ListNode(0);
        ListNode pre = ret;
        for(ListNode head : lists) {
            if(head != null) heap.offer(head);
        }
        while(!heap.isEmpty()) {
            ListNode cur = heap.poll();
            pre.next = cur;
            pre = pre.next;
            cur = cur.next;
            if(cur != null) heap.offer(cur);
        }
        return ret.next;
    }
}

5.2 分治

解题思路:

  • 与归并排序思路一模一样。
  • 直接将数组拆分为两份,两份合并之后得到两个有序链表,再合并两个有序链表即可。
  • 细节:链表数组可能为空,也可能是数组内元素为空,所以归并结束条件有所不同。

解题代码:

//时间复杂度:O(N*K*logK)
//空间复杂度:O(k)
class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        return mrege(0, lists.length-1,lists);
    }
    public ListNode mrege(int left, int right, ListNode[] lists) {
        if(left > right) return null;
        if(left == right) return lists[left];
        //分:分成两个区间
        int mid = (left + right) / 2;
        ListNode head1 = mrege(left, mid, lists);
        ListNode head2 = mrege(mid+1, right, lists);

        //合并两个有序链表
        return add(head1,head2);
    }
    public ListNode add(ListNode l1, ListNode l2) {
        if(l1 == null) return l2;
        if(l2 == null) return l1;

        ListNode newHead = new ListNode(0);
        ListNode pre = newHead;
        while(l1 != null && l2 != null) {
            if(l1.val <= l2.val) {
                pre.next = l1;
                pre = pre.next;
                l1 = l1.next;
            } else {
                pre.next = l2;
                pre = pre.next;
                l2 = l2.next;
            }
        }
        if(l1 != null) pre.next = l1;
        if(l2 != null) pre.next = l2;
        return newHead.next;
    }
}

5.3 暴力枚举

解题思路:

  • 直接遍历数组,拆分为k-1次合并两个有序链表即可。
//时间复杂度:O(N*K*K)
//空间复杂度:O(1)
class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        if(lists.length == 0) return null;
        ListNode ret = lists[0];
        for(int i = 1; i < lists.length; i++) {
            ret = add(ret, lists[i]);
        }
        return ret;
    }
    public ListNode add(ListNode l1, ListNode l2) {
        if(l1 == null) return l2;
        if(l2 == null) return l1;

        ListNode newHead = new ListNode(0);
        ListNode pre = newHead;
        while(l1 != null && l2 != null) {
            if(l1.val <= l2.val) {
                pre.next = l1;
                pre = pre.next;
                l1 = l1.next;
            } else {
                pre.next = l2;
                pre = pre.next;
                l2 = l2.next;
            }
        }
        if(l1 != null) pre.next = l1;
        if(l2 != null) pre.next = l2;
        return newHead.next;
    }
}

六、25.K个⼀组翻转链表

题目链接:25.K个⼀组翻转链表
题目描述:

题目解析:

  • 将链表每k个节点逆序,不足k个时不逆序。

解题思路:

  • 我们先遍历链表求出节点个数,再除以k得到翻转次数。
  • 然后再头插翻转即可。

解题代码:

//时间复杂度:O(n)
//空间复杂度:O(1)
class Solution {
    public ListNode reverseKGroup(ListNode head, int k) {
        int n = 0;
        ListNode cur = head;
        while(cur != null) {
            n++;
            cur = cur.next;
        }
        n /= k;

        cur = head;
        ListNode ret = new ListNode(0);
        ListNode pre = ret;
        for(int i = 0; i < n; i++) {
            ListNode tmp = cur;
            for(int j = 0; j < k; j++) {
                ListNode next = cur.next;
                cur.next = pre.next;
                pre.next = cur;
                cur = next;
            }
            pre = tmp;
        }
        pre.next = cur;
        return ret.next;
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/938671.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【面试】Redis 常见面试题

一、介绍一下什么是 Redis&#xff0c;有什么特点? Redis 是一个高性能的 key-value 内存数据库。 不同于传统的 MySQL 这样的关系型数据库&#xff0c;Redis 主要使用内存存储数据&#xff08;当然也支持持久化存储到硬盘上&#xff09;&#xff0c;并非是使用 “表” 这样…

【Linux】NET9运行时移植到低版本GLIBC的Linux纯内核板卡上

背景介绍 自制了一块Linux板卡(基于全志T113i) 厂家给的SDK和根文件系统能够提供的GLIBC的版本比较低 V2.25/GCC 7.3.1 这个版本是无法运行dotnet以及dotnet生成的AOT应用的 我用另一块同Cortex-A7的板子运行dotnet的报错 版本不够&#xff0c;运行不了 而我的板子是根本就识…

MySQL Explain 分析SQL语句性能

一、EXPLAIN简介 使用EXPLAIN关键字可以模拟优化器执行SQL查询语句&#xff0c;从而知道MySQL是如何处理你的SQL语句的。分析你的查询语句或是表结构的性能瓶颈。 &#xff08;1&#xff09; 通过EXPLAIN&#xff0c;我们可以分析出以下结果&#xff1a; 表的读取顺序数据读取…

vue3实现商城系统详情页(前端实现)

目录 写在前面 预览 实现 图片部分 详情部分 代码 源码地址 总结 写在前面 笔者不是上一个月毕业了么&#xff1f;找工作没找到&#xff0c;准备在家躺平两个月。正好整理一下当时的毕业设计&#xff0c;是一个商城系统。还是写篇文章记录下吧 预览 商品图片切换显示…

uniapp 微信小程序 功能入口

单行单独展示 效果图 html <view class"shopchoose flex jsb ac" click"routerTo(要跳转的页面)"><view class"flex ac"><image src"/static/dyd.png" mode"aspectFit" class"shopchooseimg"&g…

6.1 初探MapReduce

MapReduce是一种分布式计算框架&#xff0c;用于处理大规模数据集。其核心思想是“分而治之”&#xff0c;通过Map阶段将任务分解为多个简单任务并行处理&#xff0c;然后在Reduce阶段汇总结果。MapReduce编程模型包括Map和Reduce两个阶段&#xff0c;数据来源和结果存储通常在…

聚观早报 | 百度回应进军短剧;iPad Air将升级OLED

聚观早报每日整理最值得关注的行业重点事件&#xff0c;帮助大家及时了解最新行业动态&#xff0c;每日读报&#xff0c;就读聚观365资讯简报。 整理丨Cutie 12月18日消息 百度回应进军短剧 iPad Air将升级OLED 三星Galax S25 Ultra配色细节 一加Ace 5系列存储规格 小米…

CH582F BLE5.3 蓝牙核心板开发板 60MHz RAM:32KB ROM:448KB

CH582F BLE5.3 蓝牙核心板开发板 60MHz RAM:32KB ROM:448KB 是一款基于南京沁恒&#xff08;WCH&#xff09;推出的高性能、低功耗无线通信芯片CH582F的开发板。以下是该开发板的功能和参数详细介绍&#xff1a; 主要特性 双模蓝牙支持&#xff1a; 支持蓝牙5.0标准&#xff0…

【软件工程复习】

第1章 软件工程概述 1.2软件工程 ​ 1983年IEEE给出的定义&#xff1a;“软件工程是 开发、运行、维护和修复软件的系统方法 ” 1.4软件生存期 软件开发和运行维护由三个时期组成&#xff1a; 软件定义时期软件开发时期运行维护时期 里程碑指可以用来标识项目进程状态的事…

DuckDB: 从MySql导出数据至Parquet文件

在这篇文章中&#xff0c;介绍使用DuckDB将数据从MySQL数据库无缝传输到Parquet文件的过程。该方法比传统的基于pandas方法更高效、方便&#xff0c;我们可以从DuckDB cli实现&#xff0c;也可以结合Python编程方式实现&#xff0c;两者执行核心SQL及过程都一样。 Parquet格式…

safe area helper插件

概述 显示不同机型的必能显示的区域 实现步骤 引入safearea&#xff0c;引入其中的safearea的csharp 为cancas加入gameobject gameobject中加入safearea脚本 将UI作为这个gameobject的子物体&#xff0c;就可以完成显示

数据结构 ——二叉树转广义表

数据结构 ——二叉树转广义表 1、树转广义表 如下一棵树&#xff0c;转换为广义表 root(c(a()(b()()))(e(d()())(f()(j(h()())())))) (根&#xff08;左子树&#xff09;&#xff08;右子树&#xff09;) 代码实现 #include<stdio.h> #include<stdlib.h>//保存…

实现echart大屏动画效果及全屏布局错乱解决方式

如何实现echarts动画效果?如何实现表格或多个垂直布局的柱状图自动滚动效果?如何解决tooltip位置超出屏幕问题,如何解决legend文字过长,布局错乱问题?如何处理饼图的中心图片永远居中? 本文将主要解决以上问题,如有错漏,请指正. 一、大屏动画效果 这里的动画效果主要指&…

【YashanDB知识库】如何处理yasql输入交互模式下单行字符总量超过限制4000字节

现象 在yasql执行sql语句后报错&#xff1a;YASQL-00021 input line overflow (>4000 byte at line 4) 原因 yasql在交互模式模式下单行字符总量限制4000字节&#xff0c;超出该限制即报错。 交互式模式下&#xff0c;yasql会显示一个提示符&#xff0c;通常是 SQL>…

DALL·E 2(内含扩散模型介绍)-生成式模型【学习笔记】

视频链接&#xff1a;DALLE 2&#xff08;内含扩散模型介绍&#xff09;【论文精读】_哔哩哔哩_bilibili&#xff08;up主讲的非常好&#xff0c;通俗易懂&#xff0c;值得推荐&#xff09; 目录 1、GAN模型 2、VAE模型 2.1、AE&#xff08;Auto-Encoder&#xff09; 2.2、…

FPGA 16 ,Verilog中的位宽:深入理解与应用

目录 前言 一. 位宽的基本概念 二. 位宽的定义方法 1. 使用向量变量定义位宽 ① 向量类型及位宽指定 ② 位宽范围及位索引含义 ③ 存储数据与字节数据 2. 使用常量参数定义位宽 3. 使用宏定义位宽 4. 使用[:][-:]操作符定义位宽 1. 详细解释 : 操作符 -: 操作符 …

使用 Vue3 实现摄像头拍照功能

参考资料:MediaDevices.getUserMedia() - Web API | MDN 重要: navigator.mediaDevices.getUserMedia 需要在安全的上下文中运行。现代浏览器要求摄像头和麦克风的访问必须通过 HTTPS 或 localhost&#xff08;被视为安全的本地环境&#xff09;进行,如果上传服务器地址是http…

2024安装hexo和next并部署到github和服务器最新教程

碎碎念 本来打算写点算法题上文所说的题目&#xff0c;结果被其他事情吸引了注意力。其实我之前也有过其他博客网站&#xff0c;但因为长期不维护&#xff0c;导致数据丢失其实是我懒得备份。这个博客现在部署在GitHub Pages上&#xff0c;github不倒&#xff0c;网站不灭&…

RTMP推流平台EasyDSS在无人机推流直播安防监控中的创新应用

无人机与低空经济的关系密切&#xff0c;并且正在快速发展。2024年中国低空经济行业市场规模达到5800亿元&#xff0c;其中低空制造产业占整个低空经济产业的88%。预计未来五年复合增速将达到16.03%。 随着科技的飞速发展&#xff0c;公共安防关乎每一个市民的生命财产安全。在…

java全栈day16--Web后端实战(数据库)

一、数据库介绍 二、Mysql安装&#xff08;自行在网上找&#xff0c;教程简单&#xff09; 安装好了进行Mysql连接 连接语法&#xff1a;winr输入cmd&#xff0c;在命令行中再输入mysql -uroot -p密码 方法二&#xff1a;winr输入cmd&#xff0c;在命令行中再输入mysql -uroo…