链表-双指针-虚拟节点-力扣

链表--双指针--虚拟节点

  • 力扣 142 环形链表求循环起点 重点
  • 力扣 21 合并两个有序链表
  • 力扣 86 分割链表
  • 力扣23 合并K个有序链表 -- 优先队列(二叉堆 小顶堆)重点
  • 力扣19 找倒数第N个元素 快慢指针 + 一次遍历 重点
  • 力扣876 快慢指针找中间节点
  • 力扣 160 相交链表 遍历“两遍”
  • 辅助测试类

力扣 142 环形链表求循环起点 重点

在这里插入图片描述

/*力扣142 环形链表II* 给定一个链表的头节点  head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。* 如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。* 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。* 如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。* 不允许修改 链表。*/public static ListNode detectCycle(ListNode head) {ListNode dummy = new ListNode(-1);dummy.next = head;ListNode fast = dummy;ListNode slow = dummy;ListNode newHead = dummy;boolean symbol = false;while (fast != null && fast.next != null) {// 注意 Fast和Fast.next 都不为 null// 否则 Fast.next.next 会报空指针错误fast = fast.next.next;slow = slow.next;if (slow == fast) {//若能相等则 有环// slow 走K步 fast走K步// K = t + nS + M// 2K = t + mS + M// 非循环距离: t// 循环周长 :  S// 循环起点距离相遇点: M// 则 相遇 意味着有环 意味着 K = (m-n)S// 则 在Fast不再动的情况下 slow 再走 K 步 依然在这个相遇点// 则 其走K-M步一定在循环起点// 同理 在slow走第二个K的时候同时设置一个新的指针从头结点开始也走K-M步// 则两者应该再循环起点相遇symbol = true;break;}}if (symbol) {// 有环// slow走K-M步 fast不动 newHead 走K-M步// newHead 与 slow 在循环起点 重合while (newHead != slow) {newHead = newHead.next;slow = slow.next;}// newHead == slowreturn newHead;} else {// 无环return null;}}

力扣 21 合并两个有序链表

/*1、合并两个有序链表力扣 21将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的两个链表的节点数目范围是 [0, 50]-100 <= Node.val <= 100l1 和 l2 均按 非递减顺序 排列*/public static ListNode mergeTwoLists(ListNode l1, ListNode l2) {ListNode dummy = new ListNode(-1);// 设置虚拟头结点ListNode p = dummy;//p和dummy都是存储同一个对象地址ListNode p1 = l1, p2 = l2;while (p1 != null && p2 != null) {if (p1.val < p2.val) {p.next = p1;p1 = p1.next;p = p.next;} else {p.next = p2;p2 = p2.next;p = p.next;// p要向前进}}if (p1 != null) {p.next = p1;}if (p2 != null) {p.next = p2;}return dummy.next;}

力扣 86 分割链表

/** 给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,* 使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。* 你应当 保留 两个分区中每个节点的初始相对位置。*/public static ListNode partition(ListNode head, int x) {ListNode dummy1 = new ListNode(-1);ListNode dummy2 = new ListNode(-1);ListNode h = head;ListNode p1 = dummy1;ListNode p2 = dummy2;while (h != null) {if (h.val < x) {p1.next = h;// 向dummy1 的链表尾部添加 h节点h = h.next;// head 链表向后走一步p1 = p1.next;// p1 指向 dummy1链表末尾节点p1.next = null;// p1 末尾的next 设置为 null} else {p2.next = h;h = h.next;p2 = p2.next;p2.next = null;}}p1.next = dummy2.next;return dummy1.next;}

力扣23 合并K个有序链表 – 优先队列(二叉堆 小顶堆)重点

/*给你一个链表数组,每个链表都已经按升序排列。请你将所有链表合并到一个升序链表中,返回合并后的链表。*/public static ListNode mergeKLists(ListNode[] lists) {if (lists.length < 1) {// 如果用 < 1 的值创建优先队列 则会产生异常// IllegalArgumentException - if initialCapacity is less than 1return null;} else {PriorityQueue<ListNode> priorityQueue = new PriorityQueue<ListNode>(lists.length, new Comparator<ListNode>() {@Overridepublic int compare(ListNode o1, ListNode o2) {return o1.val - o2.val;}});// 优先级队列(二叉堆)  设置为最小堆  队列长度为lists的元素个数  有K个链表 传入 K 为队列长度ListNode dummy = new ListNode(-1); // 设置虚拟头结点 等待最后返回dummy.nextListNode p = dummy;for (ListNode listNode : lists) {if (listNode != null) {priorityQueue.add(listNode);// 如果 此链表不为空 则将头结点 存入最小堆的优先队列}}while (!priorityQueue.isEmpty()) {// 当优先队列内元素个数不为空 时ListNode temp = priorityQueue.poll();// temp 指向 队列头元素 同时队头出队p.next = temp;p = p.next;if (temp.next != null) {priorityQueue.add(temp.next); // 刚出队的链表的下一个元素 进入队列}}return dummy.next;}}

力扣19 找倒数第N个元素 快慢指针 + 一次遍历 重点

/*力扣19 给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。* 要求: 只遍历一次结点 思路:两个指针一个在前一个在后* 因为要往前走N步 设置虚拟头结点的情况下 代码更清楚简洁 不必要多判断* 只有一个元素的情况(因为如果不用虚拟头结点的话, 只有一个元素时起始节点就在此)*/public static ListNode removeNthFromEnd(ListNode head, int n) {// 题目默认head不为空 链表元素个数>=1// 先要找到倒数 第N个节点ListNode dummy = new ListNode(-1);// 设置虚拟头结点 方便向前走N步dummy.next = head;ListNode p1 = dummy;ListNode p2 = dummy;for (int i = 0; i < n; i++) {p1 = p1.next;// 向前走N步}if (p1 == null) {return null;// 链表不够长 总数都不够N}while (p1.next != null) {p1 = p1.next;p2 = p2.next;}// 当 p1 为 末尾元素时 退出循环// 此时 p2.next 指向 倒数第N个元素ListNode p3 = p2.next.next;p2.next = p3;// 删除倒数第N个元素return dummy.next;}

力扣876 快慢指针找中间节点

/*力扣876给你单链表的头结点 head ,请你找出并返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点。思路: 虚拟节点 + 快慢指针 快的一次走两步 慢的一次走一步当快的指向空的时候 慢的刚好在中间节点的位置(同时适用于奇数个元素和偶数个元素的情况)*/public static ListNode middleNode(ListNode head) {ListNode dummy = new ListNode(-1);dummy.next = head;ListNode p1 = dummy;ListNode p2 = dummy;while (p1 != null) {p2 = p2.next;// 奇数个元素时 最后一步走之前 p1.next != null && p1.next.next == null 成立// 偶数个元素时 最后一步走之前 p1.next == null 成立// 所以 让p1=p1.next.next 为一大步 的情况下// 偶数个的情况下不能走最后一大步所以下面用了break// 但P2 必须要走这一步if (p1.next == null) {break;} else {p1 = p1.next.next;}}return p2;}

力扣 160 相交链表 遍历“两遍”

/** 力扣160 相交链表* 给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。* 如果两个链表不存在相交节点,返回 null 。* 思路:两个指针同步向前走  A走到末尾之后下一步走B的头部,B走到末尾之后下一步走到A的头部* 则 两个指针会同时在第一个合并节点处同步到达 p==q 退出循环* 如果没有共同部分 则 会同时到null 依然满足p==q 退出循环 */public static ListNode getIntersectionNode(ListNode headA, ListNode headB) {/*很奇怪 我用虚拟头结点 就超时 放弃用虚拟头结点就通过力扣测试// 若用虚拟头结点 则要大量使用 p.next 而我们要 在末尾 重新转入 另一个链表表头// 故在转入的时候 不要用next 防止篡改 链表 导致死循环ListNode dummy = new ListNode(-1);dummy.next = headA;ListNode dummy2 = new ListNode(-1);dummy2.next = headB;ListNode p = dummy;ListNode q = dummy2;while (p != q) {if (p.next == null) {// 将P的位置改变 而不改变原链表结构p = headB;}if (q.next == null) {// 将Q的位置改变 而不改变原链表结构q = headA;}p = p.next;q = q.next;}return p;*/ListNode p = headA;ListNode q = headB;while (p != q) {if (p == null) {// 将P的位置改变 而不改变原链表结构p = headB;} else {p = p.next;}if (q == null) {// 将Q的位置改变 而不改变原链表结构q = headA;} else {q = q.next;}}return p;}

辅助测试类

package com.caoii.LinkedList;public class ListNode {public int val;public ListNode next;ListNode() {}public ListNode(int val) {this.val = val;}ListNode(int val, ListNode next) {this.val = val;this.next = next;}
}
package com.caoii;/**@program:labu-pratice-study*@package:com.caoii*@author: Alan*@Time: 2024/4/14  11:38*@description: 双指针解决链表问题相关应用的测试*/import com.caoii.LinkedList.DoublePointerInLinkedList;
import com.caoii.LinkedList.ListNode;
import org.junit.jupiter.api.Test;public class DoublePointerTest {/*力扣21 有序链表合并*/@Testpublic void test_01() {ListNode l1 = new ListNode(1);ListNode p1 = l1;p1.next = new ListNode(2);p1 = p1.next;p1.next = new ListNode(4);ListNode l2 = new ListNode(1);ListNode p2 = l2;p2.next = new ListNode(3);p2 = p2.next;p2.next = new ListNode(9);ListNode returnListNode = DoublePointerInLinkedList.mergeTwoLists(l1, l2);ListNode p = returnListNode;while (p != null) {System.out.print(p.val + " ");p = p.next;}System.out.println();}/*力扣86 将一个链表分割成两个链表再合并*/@Testpublic void test_02() {ListNode l1 = new ListNode(1);ListNode p1 = l1;p1.next = new ListNode(4);p1 = p1.next;p1.next = new ListNode(3);p1 = p1.next;p1.next = new ListNode(2);p1 = p1.next;p1.next = new ListNode(5);p1 = p1.next;p1.next = new ListNode(2);ListNode returnListNode = DoublePointerInLinkedList.partition(l1, 3);ListNode p = returnListNode;while (p != null) {System.out.print(p.val + " ");p = p.next;}System.out.println();}/*力扣 23 合并K个有序链表*/@Testpublic void test_03() {ListNode l1 = new ListNode(1);ListNode p1 = l1;p1.next = new ListNode(4);p1 = p1.next;p1.next = new ListNode(5);ListNode l2 = new ListNode(1);ListNode p2 = l2;p2.next = new ListNode(3);p2 = p2.next;p2.next = new ListNode(4);ListNode l3 = new ListNode(2);ListNode p3 = l3;p3.next = new ListNode(6);ListNode[] lists = new ListNode[]{l1, l2, l3};ListNode returnListNode = DoublePointerInLinkedList.mergeKLists(lists);ListNode p = returnListNode;while (p != null) {System.out.print(p.val + " ");p = p.next;}System.out.println();}/*力扣19 一次遍历 删除倒数第N个元素 */@Testpublic void test_04() {ListNode l1 = new ListNode(1);ListNode p1 = l1;p1.next = new ListNode(2);p1 = p1.next;p1.next = new ListNode(3);p1 = p1.next;p1.next = new ListNode(4);p1 = p1.next;p1.next = new ListNode(5);ListNode returnListNode = DoublePointerInLinkedList.removeNthFromEnd(l1, 2);ListNode p = returnListNode;while (p != null) {System.out.print(p.val + " ");p = p.next;}System.out.println();}/*力扣876 找链表的中间节点 奇数个找中间 偶数个找中间两个的后一个 */@Testpublic void test_05() {ListNode l1 = new ListNode(1);ListNode p1 = l1;p1.next = new ListNode(2);p1 = p1.next;p1.next = new ListNode(3);p1 = p1.next;p1.next = new ListNode(4);p1 = p1.next;p1.next = new ListNode(5);p1 = p1.next;p1.next = new ListNode(6);ListNode returnListNode = DoublePointerInLinkedList.middleNode(l1);ListNode p = returnListNode;// 输出从中间节点开始的后半截while (p != null) {System.out.print(p.val + " ");p = p.next;}System.out.println();}/*力扣142 环形链表求循环起点 */@Testpublic void test_06() {ListNode l1 = new ListNode(3);ListNode p1 = l1;p1.next = new ListNode(2);p1 = p1.next;ListNode temp = p1;// temp 辅助形成循环链表p1.next = new ListNode(0);p1 = p1.next;p1.next = new ListNode(-4);p1 = p1.next;p1.next = temp;// 形成循环链表ListNode returnListNode = DoublePointerInLinkedList.detectCycle(l1);ListNode p = returnListNode;System.out.println("循环起点的值:" + p.val);}/*力扣160 相交链表求第一个交点 */@Testpublic void test_07() {ListNode l1 = new ListNode(-3);ListNode l2 = new ListNode(30);ListNode p1 = l1;ListNode p2 = l2;ListNode temp = new ListNode(300);ListNode p3 = temp;p3.next = new ListNode(400);p3 = p3.next;p3.next = new ListNode(500);p3 = p3.next;p1.next = new ListNode(-4);p1 = p1.next;p1.next = new ListNode(-5);p1 = p1.next;p1.next = temp;p2.next = new ListNode(40);p2 = p2.next;p2.next = new ListNode(50);p2 = p2.next;p2.next = new ListNode(60);p2 = p2.next;p2.next = temp;ListNode returnListNode = DoublePointerInLinkedList.getIntersectionNode(l1, l2);ListNode p = returnListNode;System.out.println("第一个交点的值:" + p.val);}
}

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

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

相关文章

【C++】模板初阶——泛型编程、函数模板、类模板

1. 泛型编程 如何实现一个通用的交换函数呢&#xff1f; void Swap(int& left, int& right) {int temp left;left right;right temp; } void Swap(double& left, double& right) {double temp left;left right;right temp; } void Swap(char& left…

es的聚合查询(二)

1、es常用的聚合查询有三种 桶聚合 指标聚合 管道聚合 首先我们创建一个product的索引&#xff0c;并插入数据 PUT /product {"mappings": {"properties": {"category": { "type": "keyword" },"price": { "…

系统思考—时间滞延

“没有足够的时间是所有管理问题的一部分。”——彼得德鲁克 鱼和熊掌可以兼得&#xff0c;但并不能同时获得。在提出系统解决方案时&#xff0c;我们必须认识到并考虑到解决方案的实施通常会有必要的时间滞延。这种延迟有时比我们预想的要长得多&#xff0c;特别是当方案涉及…

2024年大唐杯备考

努力更新中…… 第一章 网络架构和组网部署 1.1 5G的网络整体架构 5G网络中的中传、回传、前传&#xff08;这里属于承载网的概念&#xff09; CU和DU之间是中传 BBU和5GC之间是回传 BBU和AAU之间是前传&#xff08;这个好记&#xff09; 这里竟然还藏了MEC&#xff08;…

golang的MQTT的连接操作

最近在使用golang开发后端服务的时候&#xff0c;因为需要和物联网设备进行数据交互&#xff0c;由于需要终端主动上报数据&#xff0c;也需要平台主动下发指令&#xff0c;所以我们选择了mqtt进行通信。 1、MQTT通信逻辑 MQTT采用的是订阅和发布的方式&#xff0c;数据是通过…

前端大屏适配几种方案

一、方案一&#xff1a;remfont-size 动态设置HTML根字体大小和body字体大小&#xff0c;会使用到lib-flexible.js插件lib-flexible.js (function flexible(window, document) {var docEl document.documentElementvar dpr window.devicePixelRatio || 1// adjust body font…

Element-UI 自定义-下拉框选择年份

1.实现效果 场景表达&#xff1a; 默认展示当年的年份&#xff0c;默认展示前7年的年份 2.实现思路 创建一个新的Vue组件。 使用<select>元素和v-for指令来渲染年份下拉列表。 使用v-model来绑定选中的年份值。 3.实现代码展示 <template><div><el-…

智能革命:未来人工智能创业的天地

智能革命&#xff1a;未来人工智能创业的天地 一、引言 在这个数字化迅速变革的时代&#xff0c;人工智能(AI)已经从一个边缘科学发展成为推动未来经济和社会发展的关键动力。这一技术领域的飞速进步&#xff0c;不仅影响着科技行业的每一个角落&#xff0c;更是为创业者提供了…

创建一个flutter的左划重命名,右划隐藏的功能

首先定义一下参数&#xff0c;以及左划右划的方法 double deleteButtonPosition -120;double renameButtonPosition -120;void _onHorizontalDragUpdate(DragUpdateDetails details) {setState(() {if (details.delta.dx < 0) {// 左滑deleteButtonPosition 0; // 显示删…

【vs2019】window10环境变量设置

【vs2019】window10环境变量设置 【先赞后看养成习惯】求关注点赞收藏&#x1f60a; 安装VS2019时建议默认安装地址&#xff0c;最好不要改动&#xff0c;不然容易出问题 以下是安装完VS2019后环境变量的设置情况&#xff0c;C:\Program Files (x86)\Microsoft Visual Studi…

5.3 mybatis之autoMappingUnknownColumnBehavior作用

文章目录 1. NONE2. WARNING3. FAILING autoMappingUnknownColumnBehavior是< settings >配置下的属性&#xff0c;该属性是指定发现自动映射目标未知列&#xff08;或未知属性类型&#xff09;的行为。就是说当数据库中的字段找不到映射java对象的属性或者与java对象对应…

【C++成长记】C++入门 | 类和对象(上) |类的作用域、类的实例化、类的对象大小的计算、类成员函数的this指针

&#x1f40c;博主主页&#xff1a;&#x1f40c;​倔强的大蜗牛&#x1f40c;​ &#x1f4da;专栏分类&#xff1a;C❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 目录 一、类的作用域 二、类的实例化 三、类对象模型 四、this指针 1、this指针的引出 2 this指针的特…

AI python

AI python 软件方面程序上的人工智能&#xff0c;和物理那种能跑机器人没关系

cocos creator 实现spine局部换装

1 使用3.7.4版本 2 js代码 3 c Native层修改源码

为什么你的LDO输出不稳定?

原文来自微信公众号&#xff1a;工程师看海&#xff0c;与我联系&#xff1a;chunhou0820 看海原创视频教程&#xff1a;《运放秘籍》 大家好&#xff0c;我是工程师看海。 前一阵朋友和我说当初用某型号LDO时&#xff0c;发现输出异常&#xff0c;仔细阅读datasheet后&#x…

Java可重复单列集合

Collection集合 Java Collection 是 java.util包中的一个接口&#xff0c;它用于将多个对象表示为单个单元。 Collection接口在Java中扮演着非常重要的角色&#xff0c;它提供了一种方式来存储和操作一组对象。以下是一些关于Java Collection的重要信息&#xff1a; 接口定义…

C++string类(个人笔记)

string类 1.认识string的接口以及熟练使用常用接口1.1string类对象的常见构造1.2string类对象的容量操作1.3string类对象的访问及遍历操作1.4string类对象的修改操作 2.vs 和g下string结构的说明3.string类运用的笔试题4.string类的模拟实现 1.认识string的接口以及熟练使用常用…

数据链路层(上):以太网、二层交换机和网络风暴

目录 数据链路层知识概览 数据链路层设备 1、二层交换机 2、拓展&#xff1a;二层交换机与三层交换机有啥区别&#xff1f; 3、广播风暴 4、交换机以太网接口的工作模式 数据链路层的功能 数据链路层--以太网 1、以太网是什么&#xff1f; 2、以太网地址 数据链路层知…

bugku-web-安慰奖

提示备份 开始扫后台 得到备份文件index.php.bak 得到php代码 <?phpheader("Content-Type: text/html;charsetutf-8"); error_reporting(0); echo "<!-- YmFja3Vwcw -->"; class ctf {protected $username hack;protected $cmd NULL;public f…

【python】 numpy 中常用随机数函数的使用和记忆(不易混淆)

文章目录 概述固定分布随机数&#xff08;只需指定形状的随机函数&#xff09;np.random.randomnp.random.randnp.random.randn 随机范围随机数&#xff08;需要指定范围和形状的随机函数&#xff09;np.random.randintnp.random.uniformnp.random.normalnp.random.poisson 代码…