【剑指offer刷题记录 java版】链表双指针

news2024/12/23 17:51:23

本系列文章记录labuladong的算法小抄中剑指offer题目


【剑指offer刷题记录 java版】链表双指针

  • 剑指 Offer II 025. 链表中的两数相加
  • 剑指 Offer 25. 合并两个排序的链表
  • 剑指 Offer 52. 两个链表的第⼀个公共节点
  • 剑指 Offer II 021. 删除链表的倒数第 n 个结点
  • 剑指 Offer II 022. 链表中环的⼊⼝节点
  • 剑指 Offer II 023. 两个链表的第⼀个重合节点
  • 剑指 Offer II 078. 合并排序链表(困难)
  • 剑指 Offer 49. 丑数
  • 剑指 Offer 18. 删除链表的节点
  • 剑指 Offer II 061. 和最⼩的 k 个数对
  • 剑指 Offer II 027. 回⽂链表
  • 总结


剑指 Offer II 025. 链表中的两数相加

题目链接:https://leetcode.cn/problems/lMSNwu/
本题基础题:https://leetcode.cn/problems/add-two-numbers/ 基础题解法:

class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        ListNode p=l1,q=l2;//双指针
        int temp = 0;//进位
        ListNode res = new ListNode();//虚拟头节点(用于返回)
        ListNode node = res;//结果指针
        while(p!=null || q!=null || temp!=0){
            int val = temp;//加上进位
            if(p!=null){
                val+=p.val;
                p=p.next;
            }
            if(q!=null){
                val+=q.val;
                q=q.next;
            }
            temp=val/10;//记录本次进位
            node.next=new ListNode();
            node=node.next;
            node.val=val%10;
        }
        return res.next;
    }
}

在这里插入图片描述
在这里插入图片描述
利用栈的特性存储链表每个节点的数据。

class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        Deque<Integer> s1 = new LinkedList();
        Deque<Integer> s2 = new LinkedList();
        while(l1!=null){
            s1.offerLast(l1.val);
            l1=l1.next;
        } 
        while(l2!=null){
            s2.offerLast(l2.val);
            l2=l2.next;
        } 
        int temp=0;//记录进位
        ListNode res = new ListNode();//结果指针
        
        while(!s1.isEmpty() || !s2.isEmpty() || temp!=0){
            int val = temp;
            if(!s1.isEmpty()){
                val += s1.pollLast();
            }
            if(!s2.isEmpty()){
                val += s2.pollLast();
            }
            temp = val/10;
            ListNode node = new ListNode(val%10);
            node.next = res.next;
            res.next = node;
        }
        return res.next;
    }
}

剑指 Offer 25. 合并两个排序的链表

题目链接:https://leetcode.cn/problems/he-bing-liang-ge-pai-xu-de-lian-biao-lcof/
在这里插入图片描述

class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        ListNode p1=l1,p2=l2;
        ListNode res = new ListNode();
        ListNode node = res;
        while(p1!=null && p2!=null){
            if(p1.val<p2.val){
                node.next = p1;
                p1=p1.next;
            }else{
                node.next = p2;
                p2=p2.next;
            }
            node = node.next;
        }
        if(p1!=null){
            node.next = p1;
        }
        if(p2!=null){
            node.next = p2;
        }
        return res.next;
    }
}

剑指 Offer 52. 两个链表的第⼀个公共节点

题目链接:https://leetcode.cn/problems/liang-ge-lian-biao-de-di-yi-ge-gong-gong-jie-dian-lcof/
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在逻辑上连接两个链表,使相交部分再其后半部分对齐。
在这里插入图片描述

class Solution {
    ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode p1=headA, p2=headB;
        while(p1!=p2){
            if(p1==null){
                p1=headB;
            }else{
                p1=p1.next;
            }
            
            if(p2==null){
                p2=headA;
            }else{
                p2=p2.next;
            }
        }
        return p1;
    }
}

剑指 Offer II 021. 删除链表的倒数第 n 个结点

题目链接:https://leetcode.cn/problems/SLwz0R/
在这里插入图片描述在这里插入图片描述
本题解法类似固定长度的滑动窗口。需要使用虚拟头节点避免空指针的状况。

class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        // 虚拟头结点
        ListNode dummy = new ListNode(-1);
        dummy.next = head;

        ListNode p1 = dummy;
        //先走n+1步
        for (int i = 0; i < n+1; i++) {
            p1 = p1.next;
        }
        ListNode p2 = dummy;
        while(p1!=null){
            p1=p1.next;
            p2=p2.next;
        }
        p2.next=p2.next.next;
        return dummy.next;
    }
}

剑指 Offer II 022. 链表中环的⼊⼝节点

基础题:https://leetcode.cn/problems/linked-list-cycle/ 链表找环解法:

public class Solution {
    public boolean hasCycle(ListNode head) {
        ListNode slow = head, fast = head;
        while(fast!=null && fast.next!=null){
            slow=slow.next;
            fast=fast.next.next;
            if(slow==fast){return true;}
        }
        return false;
    }
}

题目链接:https://leetcode.cn/problems/c32eOV/
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
先快慢移动,相交后把一个放回起点,另一个再交点,这两个指针以相同速度移动,第二次相交位置即为环的起点。

public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode slow = head, fast = head;
        while(fast!=null && fast.next!=null){
            slow=slow.next;
            fast=fast.next.next;
            if(slow==fast){
                slow=head;
                while(slow!=fast){
                    slow=slow.next;
                    fast=fast.next;
                }
                return slow;
            }
        }
        return null;
    }
}

剑指 Offer II 023. 两个链表的第⼀个重合节点

题目链接:https://leetcode.cn/problems/3u1WK4/
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
进阶:能否设计一个时间复杂度 O(n) 、仅用 O(1) 内存的解决方案?
【思路】两个指针分别遍历链表AB和链表BA,因为不同的元素数量一致,因此交点会在后半部分对齐。

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode p1=headA,p2=headB;
        while(p2!=p1){
        	//遍历链表AB
            if(p1==null){
                p1=headB;
            }else{
                p1=p1.next;
            }
			//遍历链表AB
            if(p2==null){
                p2=headA;
            }else{
                p2=p2.next;
            }
        }
        return p1;//如果没有交点,最后两个指针都为null
    }
}

剑指 Offer II 078. 合并排序链表(困难)

题目链接:https://leetcode.cn/problems/vvXgSW/
在这里插入图片描述
在这里插入图片描述

利用优先级队列,每次输出当前最小的节点
优先级队列基础知识

class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        if (lists.length == 0) return null;
        // 虚拟头结点
        ListNode dummy = new ListNode(-1);
        ListNode p = dummy;
        // 优先级队列,最小堆
        PriorityQueue<ListNode> pq = new PriorityQueue<>(
            lists.length, (a, b)->(a.val - b.val));
        // 将 k 个链表的头结点加入最小堆
        for (ListNode head : lists) {
            if (head != null)
                pq.add(head);
        }

        while (!pq.isEmpty()) {
            // 获取最小节点,接到结果链表中
            ListNode node = pq.poll();
            p.next = node;
            if (node.next != null) {
                pq.add(node.next);
            }
            // p 指针不断前进
            p = p.next;
        }
        return dummy.next;
    }
}

剑指 Offer 49. 丑数

基础题:263. 丑数
基础题解法:

class Solution {
    public boolean isUgly(int n) {
        if (n <= 0) return false;
        // 如果 n 是丑数,分解因子应该只有 2, 3, 5
        while(n%2==0){n/=2;}//该数内部还有因数2时,除以2
        while(n%3==0){n/=3;}//该数内部还有因数3时,除以3
        while(n%5==0){n/=5;}//该数内部还有因数5时,除以5
        return n==1;
    }
}

题目链接:https://leetcode.cn/problems/chou-shu-lcof/
在这里插入图片描述
巧妙地合并三个链表,具体思路可以参考264. 丑数 II 中labuladong的可视化过程

class Solution {
    public int nthUglyNumber(int n) {
        // 可以理解为三个指向有序链表头结点的指针
        int p2 = 1, p3 = 1, p5 = 1;
        // 可以理解为三个有序链表的头节点的值
        int product2 = 1, product3 = 1, product5 = 1;
        // 可以理解为最终合并的有序链表(结果链表)
        int[] ugly = new int[n + 1];
        // 可以理解为结果链表上的指针
        int p = 1;

        // 开始合并三个有序链表
        while (p <= n) {
            // 取三个链表的最小结点
            int min = Math.min(Math.min(product2, product3), product5);
            // 接到结果链表上
            ugly[p] = min;
            p++;
            // 前进对应有序链表上的指针
            if (min == product2) {
                product2 = 2 * ugly[p2];
                p2++;
            }
            if (min == product3) {
                product3 = 3 * ugly[p3];
                p3++;
            }
            if (min == product5) {
                product5 = 5 * ugly[p5];
                p5++;
            }
        }
        // 返回第 n 个丑数
        return ugly[n];
    }
}

剑指 Offer 18. 删除链表的节点

题目链接:https://leetcode.cn/problems/shan-chu-lian-biao-de-jie-dian-lcof/
在这里插入图片描述

class Solution {
    public ListNode deleteNode(ListNode head, int val) {
        ListNode dummy = new ListNode();
        dummy.next=head;
        ListNode p = dummy;
        while(p!=null && p.next!=null){
            if(p.next.val==val){
                p.next=p.next.next;
            }
            p=p.next;
        }
        return dummy.next;
    }
}

剑指 Offer II 061. 和最⼩的 k 个数对

题目链接:https://leetcode.cn/problems/qn8gGX/
在这里插入图片描述
在这里插入图片描述

本质也是合并K个有序链表,可以用优先级队列处理。

class Solution {
    public List<List<Integer>> kSmallestPairs(int[] nums1, int[] nums2, int k) {
        // 存储三元组 (num1[i], nums2[i], i)
        // i 记录 nums2 元素的索引位置,用于生成下一个节点
        PriorityQueue<int[]> pq = new PriorityQueue<>((a, b) -> {
            // 按照数对的元素和升序排序
            return (a[0] + a[1]) - (b[0] + b[1]);
        });
        // 按照 23 题的逻辑初始化优先级队列(将每个链表的链表头加入优先级队列)
        for (int i = 0; i < nums1.length; i++) {
            pq.offer(new int[]{nums1[i], nums2[0], 0});
        }

        List<List<Integer>> res = new ArrayList<>();
        // 执行合并多个有序链表的逻辑
        while (!pq.isEmpty() && k > 0) {
            int[] cur = pq.poll();
            k--;
            // 链表中的下一个节点加入优先级队列
            int next_index = cur[2] + 1;
            if (next_index < nums2.length) {
                pq.add(new int[]{cur[0], nums2[next_index], next_index});
            }

            List<Integer> pair = new ArrayList<>();
            pair.add(cur[0]);
            pair.add(cur[1]);
            res.add(pair);
        }
        return res;
    }
}

剑指 Offer II 027. 回⽂链表

题目链接:https://leetcode.cn/problems/aMhZSa/
在这里插入图片描述
在这里插入图片描述

方法一:时间复杂度 O(n) ,空间复杂度 O(n) ,将值复制到数组中后用双指针法

class Solution {
    public boolean isPalindrome(ListNode head) {
        List<Integer> vals = new ArrayList<Integer>();

        // 将链表的值复制到数组中
        ListNode currentNode = head;
        while (currentNode != null) {
            vals.add(currentNode.val);
            currentNode = currentNode.next;
        }

        // 使用双指针判断是否回文
        int front = 0;
        int back = vals.size() - 1;
        while (front < back) {
            if (!vals.get(front).equals(vals.get(back))) {
                return false;
            }
            front++;
            back--;
        }
        return true;
    }
}

方法二:时间复杂度 O(n) ,空间复杂度 O(1) ,双指针找到中间节点,反转链表然后对比

class Solution {
    public boolean isPalindrome(ListNode head) {
        ListNode slow=head,fast=head;
        while(fast!=null && fast.next!=null){
            slow=slow.next;
            fast=fast.next.next;
        }
        if(fast!=null){
            // 链表有奇数个节点
            slow=slow.next;
        }else{
            // 链表有偶数个节点,什么都不做
        }

        // 从slow开始反转后半部分链表
        ListNode pre = null;
        ListNode cur = slow;
        while(cur!=null){
            ListNode next=cur.next;
            cur.next=pre;
            pre =cur;
            cur = next;
        }
        //此时pre为反转后链表的头节点
        while(pre!=null){
            if(head.val!=pre.val){
                return false;
            }
            head=head.next;
            pre=pre.next;
        }
        return true;
    }
}

总结

  1. 利用虚拟头节点可以简化链表的创建,创建时始终记得要有一个指针。
  2. 链表的增删改可以通过虚拟头节点来统一。
  3. 合并 k 个有序链表可以利用优先级队列来实现,流程是:先将所有链表的头节点传入优先级队列,没弹出一个就补上该链表的下一个元素,直至满足输出需求。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

qt event事件处理

qt事件处理 qt事件处理比较恶心&#xff0c;各个事件都是独立的。如果同一时间出现多个事件&#xff0c;某些事件在qt中接收不到。 可以参考qtbase源码事件处理部分&#xff0c;所有的事件都在switch…case中处理&#xff0c;所以一次循环只会处理一个事件。 主窗口中可以通过…

【Rust 基础篇】Rust引用详解

文章目录 引言一、什么是引用&#xff1f;二、不可变引用三、可变引用四、引用的规则五、引用的使用建议六、示例代码总结 引言 在Rust中&#xff0c;引用是一种轻量级的指向数据的方式&#xff0c;它允许我们在不获取所有权的情况下访问和操作数据。引用是Rust中处理借用操作…

PostgreSql 逻辑结构

Database Cluser: 数据库集簇&#xff0c;一套服务器上安装部署完成的一套PostgreSql。在其中可创建数据库&#xff08;Database&#xff09;、用户&#xff08;User&#xff09;。User: 数据库用户&#xff0c;用来连接访问数据库&#xff0c;可通过权限管理&#xff0c;控制其…

360手机刷机 360手机Magisk面具安装与使用教程

360手机刷机 360手机Magisk面具安装与使用教程 参考&#xff1a;360手机-360刷机360刷机包twrp、root 360刷机包360手机刷机&#xff1a;360rom.github.io 【前序】 360手机通过Twrp&#xff0c;即可刷写Magisk文件&#xff1b;刷写成功后&#xff0c;即可获得root权限&#…

idm下载器怎么样好用吗?最新版本有哪些优势

日常工作中下载资料、音/视频等文件是常见的操作&#xff0c;如今市面上的软件非常多&#xff0c;根据我个人的使用经验idm非常不错。idm下载软件怎么样&#xff1f;idm下载软件不仅可下载的文件类型多&#xff0c;而且idm下载文件的速度非常快&#xff0c;同样下载文件的方法也…

电子证件照怎么弄?学会这几招在家也能做证件照

在很多情况下&#xff0c;人们需要制作证件照来证明自己的身份。例如&#xff0c;如果你想办理身份证、护照、驾驶证等证件&#xff0c;或者报考各类考试或申请学校、公司等机构&#xff0c;或者办理银行卡、社保卡等业务&#xff0c;或者申请签证或出入境手续&#xff0c;或者…

基于Java+Swing+Mysql商品信息管理系统

基于JavaSwingMysql商品信息管理系统 一、系统介绍二、功能展示1.主页2.新增商品信息3.查询商品信息 三、数据库四、其他系统实现五、获取源码 一、系统介绍 该系统实现了查看商品列表、新增商品信息、查询商品信息 运行环境&#xff1a;eclipse、idea、jdk1.8 二、功能展示…

IOC-DI(分层解耦)

问题-引出 可以发现我们之前的代码但是写在我们的controller程序中 这里因为比较简单 但是如果我们开发一个比较复杂的功能的话-会出现大量操作数据的代码 导致代码的复用性较差 且难以维护 分层解耦 三层架构 按照上面的对应代码不同功能 来分为下面这三个架构 对应的contr…

Elasticsearch:跨集群复制应用场景及实操 - Cross Cluster Replication

通过跨集群复制&#xff08;Cross Cluster Replication - CCR&#xff09;&#xff0c;你可以跨集群将索引复制并实现&#xff1a; 在数据中心中断时继续处理搜索请求防止搜索量影响索引吞吐量通过在距用户较近的地理位置处理搜索请求来减少搜索延迟 跨集群复制采用主动 - 被…

电商神器!教你如何利用数据分析打造销售奇迹!

能解决80%通用需求&#xff0c;提供销售、财务、广告、库存等电商数据分析主题的奥威BI电商数据分析方案一直都是比较神秘的存在。有说它风险低的&#xff0c;也有说它性价比高、效率高、可塑性高&#xff08;支持个性化开发&#xff09;&#xff0c;但说到底&#xff0c;这份B…

企业机房如何管理电池?分享一个实操方法

在现代的信息化社会中&#xff0c;机房扮演着至关重要的角色&#xff0c;是许多组织和企业的核心基础设施。而机房的稳定供电则离不开可靠的电池系统作为备用电源。 电池的状态监控和管理一直是机房管理者面临的挑战。为了确保机房的可靠运行和及时发现电池问题&#xff0c;动环…

二、部署Git服务器-Windows环境部署Gitea

目录 1. 说明2. 环境准备3. 安装部署3.1 安装Git3.2 安装Gitea3.3 将 Gitea 注册为 Windows 服务&#xff08;可选&#xff09;3.4 启用 Gitea 内置的 SSH 服务器&#xff08;可选&#xff09;3.5 编辑 Windows 防火墙 Gitea是一个自托管的Git服务&#xff0c;类似于GitHub、Gi…

设计模式学习之策略模式和简单工厂模式的对比

设计模式系列往期文章 设计模式学习之策略模式设计模式学习之策略模式在前端的应用设计模式学习之简单工厂模式设计模式学习之工厂方法模式设计模式学习之抽象工厂模式 在这之前我们已经介绍过了策略模式和简单工厂模式&#xff0c;也清楚策略模式属于行为模式&#xff0c;简…

mysql字符集小总结

1.先看部分字符集 show variables like ‘character%’; 解释一下&#xff1a; character_set_client //客户端字符集 character_set_connection //连接字符集 character_set_database //mysql数据库字符集 character_set_filesystem //文件系统字符集 character_set_res…

普通相机标定——OpenCV函数讲解(上)

计算流程 我们使用OpenCV进行标定算法的开发,这里使用的是OpenCV3.4.6版本。使用OpenCV标定相机的算法流程如下。 Step1.检测之前采集的多张图像中的棋盘格角点像素坐标 Step2.利用棋盘格角点的像素坐标、世界坐标,计算相机的内参、畸变系数 Step3.利用相机的内参…

微信小程序web-view嵌入uni-app H5页面,通过H5页面跳转企业微信客户聊天窗口如何操作?

1、先找到企业ID和企业微信聊天接入链接 1&#xff09;找到企业ID&#xff0c;登录 企业微信 https://work.weixin.qq.com/https://work.weixin.qq.com/ 2&#xff09;找到接入链接 2、开始写H5页面代码 let d {corpId: ww931你挚爱的强哥8dee,//企业微信IDurl: https://…

[Visual Studio]Ctrl + T 跳转没有预览下拉选项的问题

最近一段时间&#xff0c;在用VS的CtrlT的跳转时&#xff0c;发现看不到下面的预览选项了。各种方式尝试无效。 最后发现原来下面的紫色区域&#xff0c;将鼠标悬停上去&#xff0c;是可以下拉的…

JavaScript(JS基础)

提示&#xff1a;JS的重点是让静态的页面产生一些变化 文章目录 JS编写方式嵌入编写外部编写 JS基本语法使用let声明变量使用var声明变量 JS中的数据类型JS中运算符❗ 和 JS中的数据类型转换字符串类型加减乘除布尔类型加减乘除 字符串与整数、小数之间的转换 JS流程控制语句 J…

【javascript】二维码

javascript二维码的生成可以用第三方库qrcode.js。 下载地址&#xff1a;https://gitcode.net/mirrors/davidshimjs/qrcodejs 解压后打开index.html文件输入百度地址回车&#xff0c;就可以看到指定页面的二维码了。 html代码&#xff1a; <!DOCTYPE html PUBLIC "-/…

2.Elasticsearch核心概念

一、Es中的核心概念 elasticsearch中有很多独有的概念,与mysql中略有差别,但也有相似之处 1.1文档和字段 一个文档就像数据库里的一条数据,字段就像数据库里的列 elasticsearch是面向文档(Document)存储的,可以是数据库中的一条商品数据,一个订单信息。文档数据会被序…