20230422 | 24. 两两交换链表中的节点、19.删除链表的倒数第N个节点、面试题 02.07. 链表相交、142. 环形链表 II

news2024/11/18 7:44:43

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

在这里插入图片描述
初始时,cur指向虚拟头结点,然后进行如下三步:
在这里插入图片描述
操作之后,链表如下:

在这里插入图片描述

看这个可能就更直观一些了:
在这里插入图片描述

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode swapPairs(ListNode head) {
        //设置一个虚拟头节点
        ListNode dump = new ListNode(-1);
        //将虚拟头节点指向head,这样方便后面做删除操作
        dump.next = head;
        ListNode cur = dump; //当前节点
        ListNode firstNode ; //临时节点,保存两个节点之间的第一个节点
        ListNode secondNode;//临时节点,保存两个节点之间的第二个节点
        while(cur.next!=null && cur.next.next!=null){//确保有三个节点才进行
            ListNode temp = cur.next.next.next;//临时节点,保存两个节点后面的节点,即第三个节点
            firstNode = cur.next;
            secondNode = cur.next.next;
            cur.next = secondNode; //步骤一
            secondNode.next = firstNode;//步骤二
            firstNode.next = temp;//步骤三
            cur = firstNode; //cur移动,准备下一轮交换
        }
        return dump.next; //除去头节点的后一个节点为开始节点
    }
}

2、19.删除链表的倒数第N个节点

在这里插入图片描述

  • 双指针的经典应用,如果要删除倒数第n个节点,让fast移动n步,然后让fast和slow同时移动,直到fast指向链表末尾。删掉slow所指向的节点就可以了。
  • 这里我使用虚拟头结点,这样方便处理删除实际头结点的逻辑,
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        if(head==null) return head;
        ListNode dump = new ListNode(-1);
        dump.next = head;
        //定义快慢指针
        ListNode fastNode = dump;
        ListNode slowNode = dump;
        //使用快慢指针 快的先走n步
        for(int i =0;i<n;i++){
            fastNode = fastNode.next;
        }
        //然后快慢指针一起走,快指针到尾部时,慢指针刚好指向要删除节点的前一个
        while(fastNode.next!=null){
            fastNode = fastNode.next;
            slowNode = slowNode.next;
        }

        //slowNode指向要删除节点的前一个
        slowNode.next = slowNode.next.next;

        //返回
        return dump.next;
    }
}

3、面试题 02.07. 链表相交

在这里插入图片描述
在这里插入图片描述

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        if(headA==null || headB==null) return null;
     
        //思路:先确定headA和headB的长度,求他们的差值,长的先走,然后再一起走,遇到相同的直接返回
        int lenA =0;
        int lenB =0;
        ListNode curA = headA;
        ListNode curB = headB;
        while(curA!=null){
            lenA++;
            curA = curA.next;
        }
        while(curB!=null){
            lenB++;
            curB = curB.next;
        }
        int len = 0;
        curA = headA; //再次指向头节点
        curB = headB; //再次指向头节点
        if(lenA>lenB){
            len = lenA-lenB;
            //长的先走
            for(int i=0;i<len;i++){
                curA=curA.next;
            }
            //再一起走,遇到相同的直接返回
            while(curA!=null){
                if(curA == curB){
                    return curA;
                }
                curA = curA.next;
                curB = curB.next;
            }
        }else{
            len = lenB-lenA;
            //长的先走
            for(int i=0;i<len;i++){
                curB=curB.next;
            }
            //再一起走,遇到相同的直接返回
             while(curA!=null){
                if(curA == curB){
                    return curA;
                }
                curA = curA.next;
                curB = curB.next;
            }
        }
        return null;
    }
}

备注记录一下错误

if(headA==null ) return headB; 错误原因:是找公共交点,我返回的什么??,应该返回 if(headA==null ) return null

//再一起走,遇到相同的直接返回
while(curA.next!=null){}与 while(curA!=null){}的区别
while(curA.next!=null){}比 while(curA!=null){}少判断一次

4、142. 环形链表 II

判断链表是否有环

可以使用快慢指针法,分别定义 fast 和 slow 指针,从头结点出发,fast指针每次移动两个节点,slow指针每次移动一个节点,如果 fast 和 slow指针在途中相遇 ,说明这个链表有环。

  • 为什么fast 走两个节点,slow走一个节点,有环的话,一定会在环内相遇呢,而不是永远的错开呢

  • 首先第一点:fast指针一定先进入环中,如果fast指针和slow指针相遇的话,一定是在环中相遇,这是毋庸置疑的。

  • 那么来看一下,为什么fast指针和slow指针一定会相遇呢?

  • 可以画一个环,然后让 fast指针在任意一个节点开始追赶slow指针。

  • 会发现最终都是这种情况, 如下图:
    在这里插入图片描述

  • fast和slow各自再走一步, fast和slow就相遇了

  • 这是因为fast是走两步,slow是走一步,其实相对于slow来说,fast是一个节点一个节点的靠近slow的,所以fast一定可以和slow重合。

如果有环,如何找到这个环的入口

  • 此时已经可以判断链表是否有环了,那么接下来要找这个环的入口了。

  • 假设从头结点到环形入口节点 的节点数为x。 环形入口节点到 fast指针与slow指针相遇节点 节点数为y。 从相遇节点 再到环形入口节点节点数为 z。 如图所示:
    在这里插入图片描述

  • 那么相遇时: slow指针走过的节点数为: x + y, fast指针走过的节点数:x + y + n (y + z),n为fast指针在环内走了n圈才遇到slow指针, (y+z)为 一圈内节点的个数A。

  • 因为fast指针是一步走两个节点,slow指针一步走一个节点, 所以 fast指针走过的节点数 = slow指针走过的节点数 * 2:

  • (x + y) * 2 = x + y + n (y + z)

  • 两边消掉一个(x+y): x + y = n (y + z)

  • 因为要找环形的入口,那么要求的是x,因为x表示 头结点到 环形入口节点的的距离。

  • 所以要求x ,将x单独放在左面:x = n (y + z) - y ,

  • 再从n(y+z)中提出一个 (y+z)来,整理公式之后为如下公式:x = (n - 1) (y + z) + z 注意这里n一定是大于等于1的,因为 fast指针至少要多走一圈才能相遇slow指针。

  • 这个公式说明什么呢?

  • 先拿n为1的情况来举例,意味着fast指针在环形里转了一圈之后,就遇到了 slow指针了。

  • 当 n为1的时候,公式就化解为 x = z,

  • 这就意味着,从头结点出发一个指针,从相遇节点 也出发一个指针,这两个指针每次只走一个节点, 那么当这两个指针相遇的时候就是 环形入口的节点。

  • 也就是在相遇节点处,定义一个指针index1,在头结点处定一个指针index2。

  • 让index1和index2同时移动,每次移动一个节点, 那么他们相遇的地方就是 环形入口的节点。

  • 那么 n如果大于1是什么情况呢,就是fast指针在环形转n圈之后才遇到 slow指针。

  • 其实这种情况和n为1的时候 效果是一样的,一样可以通过这个方法找到 环形的入口节点,只不过,index1 指针在环里 多转了(n-1)圈,然后再遇到index2,相遇点依然是环形的入口节点。

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode slow = head;
        ListNode fast = head;
        //快指针每次走两步,满指针每次走一步
        while(fast!=null && fast.next!=null){
            slow = slow.next;
            fast = fast.next.next;
            if(slow == fast){//有环
                ListNode index1 = fast;
                ListNode index2 = head;
                //两个指针,从头节点和相遇节点,各走一步,直至相遇,相遇点即为环入口
                while(index1!=index2){
                    index1 = index1.next;
                    index2 = index2.next;
                }
                return index1;
            }
            
        }
        return null;
    }
}

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

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

相关文章

camunda流程引擎send task节点用途

Camunda的Send Task用于向外部系统或服务发送消息。消息可以是同步或异步的&#xff0c;可以发送到队列、主题或其他类型的消息中间件。Send Task通常用于将消息发送到外部系统&#xff0c;而无需等待响应或结果。相反&#xff0c;它只是向外部系统发出信号&#xff0c;通知其执…

使用Storm proxies动态代理IP如何正确设置使用海外IP代理?

正确设置使用海外IP代理需要以下几个步骤&#xff1a; 获取代理服务器的IP地址和端口号&#xff1a;可以在代理服务提供商的网站上或者代理IP池中获取相应的信息。在计算机或移动设备上配置代理服务器&#xff1a;打开网络设置&#xff0c;找到代理服务器的设置选项&#xff0c…

10. 并查集

10. 并查集 并查集是一种树型的数据结构 &#xff0c;并查集可以高效地进行如下操作&#xff1a; 查询元素p和元素q是否属于同一组 合并元素p和元素q所在的组 10.1 并查集结构 并查集也是一种树型结构&#xff0c;但这棵树跟我们之前讲的二叉树、红黑树、B树等都不一样&…

项目风险管理的5个重点 不得不重视

风险管理持续贯穿软件项目的整个生命周期&#xff0c;其对项目的影响非常大&#xff0c;那么如何高效管理项目风险&#xff1f;5个风险管理重点如下&#xff1a; 1、风险识别和科学分析 需要对风险发生的可能性进行分析&#xff0c;判断风险对项目影响可能性并记录其特征&#…

【 初识 Spring MyBatis 查询数据库 】

文章目录 一、概念二、为什么学 MyBatis三、怎么学 MyBatis四、第⼀个MyBatis查询4.1 MyBatis 在整个框架中的定位4.2 准备&#xff1a;创建库和表4.3 配置 MyBatis 开发环境4.3.1 添加MyBatis框架⽀持4.3.1.1 ⽼项⽬添加支持扩展&#xff1a;在⽼项⽬中快速添加框架 - EditSta…

ChatGPT 速通手册——不同相似度算法的分值介绍

不同相似度算法的分值介绍 在信息大暴涨的今天&#xff0c;人类已经不可能出现通才、全才式的人物。利用 ChatGPT 来询问我们未知领域的知识是很好的习惯和用法。但对严肃知识的学习&#xff0c;一定要通过权威来源复核审校&#xff0c;保证自己所学知识的正确。否则&#xff…

【安全学习笔记】信息收集-CDN相关的技术(CDN绕过)

CDN相关的技术&#xff08;CDN绕过&#xff09; CDN&#xff1a;内容分发网络&#xff0c;它是构建在现有网络基础之上的智能虚拟网络&#xff0c;依靠部署在各地的边缘服务器&#xff0c;通过中心平台的负载均衡、内容分发、调度等功能模块&#xff0c;使用户就近获取所需内容…

104. 二叉树的最大深度【75】

难度等级&#xff1a;容易 上一篇算法&#xff1a; 101. 对称二叉树【74】 力扣此题地址&#xff1a; 104. 二叉树的最大深度 - 力扣&#xff08;Leetcode&#xff09; 1.题目&#xff1a;104. 二叉树的最大深度 给定一个二叉树&#xff0c;找出其最大深度。 二叉树的深度为根…

【并发编程】Java并发之关键字synchronized使用和原理

文章目录 前言一、synchronized的四种应用方式修饰一个代码块修饰一个方法修饰一个静态的方法修饰一个类 二、synchronized底层语义原理三、理解Java对象头与Monitor四、synchronized代码块底层原理五、synchronized方法底层原理六、Java虚拟机对synchronized的优化偏向锁轻量级…

Finetuner+:为企业实现大模型微调和私有化部署

如 ChatGPT、GPT4 这样的大型语言模型就像是你为公司请的一个牛人顾问&#xff0c;他在 OpenAI、Google 等大公司被预训练了不少的行业内专业知识&#xff0c;所以加入你的公司后&#xff0c;你只需要输入 Prompt 给他&#xff0c; 介绍一些业务上的背景知识&#xff0c;他就能…

清除Github提交历史commit

如果提交代码到Github仓库时&#xff0c;不小心把敏感信息&#xff08;比如登陆账号和登陆密码&#xff09;提交了上去&#xff0c;尽快处理。 git log 查看提交记录 git log定位你误操作的那一个版本 ‘be757abcb2b6c2b86b489384aeb4619d9b8c94c7’ 比如这个是你提交版本的…

2023全网汇总PMP备考攻略(附答题技巧)

一&#xff0c;多复习和学习新版考纲 01《PMBOK》看三遍 这边建议看三遍《PMBOK》&#xff0c;更有利于我们巩固知识&#xff0c;查缺补漏。 第一遍 第一遍是老师带着我们去看。这个时候一定要非常专心&#xff0c;千万不要上课走神或者玩手机。因为这一遍老师会告诉我们&a…

FreeRTOS源码获取以及解释各个文件作用

1.源码可以在官网:FreeRTOS官网&#xff1a;https://www.freertos.org/下载 2.FreeRTOS源码内容介绍 名称 描述 FreeRTOS FreeRTOS内核 FreeRTOS-Plus FreeRTOS组件 tools 工具 GitHub-FreeRTOS-Home FreeRTOS的GitHub仓库链接 Quick_Start_Guide 快速入门指南官方…

C/C++ Linux进程操作

目录 一、简介 二、创建进程 1. fork 2. wait 3. exit 三、多进程高并发设计 四、孤儿进程 五、僵尸进程 六、守护进程 七、总结 一、简介 进程是什么&#xff1f; 答&#xff1a;可以简单理解为&#xff0c;一个 .exe 的应用程序&#xff0c;就是运行在进程中的&a…

ChatGPT时代,我们可能站到了自然语言编程的大门口

ChatGPT大火&#xff0c;我现在有种感觉&#xff1a;我们可能站到了自然语言编程的门口&#xff0c;一脚下去&#xff0c;也许能把门踹开。 当然&#xff0c;也可能会踢到一块铁板。 回顾我们的编程之路&#xff0c;基本上就是一个编程门槛不断降低的历史。 最早的一批前辈们…

OSCP-Fail(rsync、fail2ban提权)

目录 扫描 rsync 提权 扫描 rsync 基于nmap,确信将进一步研究rsync。 为此,将使用netcat使用的rsync枚举。 使用netcat,我们可以列出rsync托管的当前共享。 我们看到“fox”和“fox home

万字长文带我弄懂JS基础!!!

js的学习笔记 文章目录 js的学习笔记JS基础篇js 的输出js的基本语法js语句js的注释js的变量js数据类型简介js对象简介js函数简介js的作用域js中的事件js字符串js运算符js条件语句js循环for/in循环 js的break和continuejs之typedefnullundefinedjs类型转换**constructor属性**St…

人类思维VS AI智能:谁是未来的胜者?

在“人工智能&#xff08;AI&#xff09;是否会取代人类”的问题上&#xff0c;谷歌的首席执行官埃德拉里博斯&#xff08;Ed Larrabee&#xff09;说&#xff1a;“我不认为 AI会取代人类。”而英国首相鲍里斯约翰逊则认为&#xff1a;“我们不能让 AI成为我们的敌人。”现在&…

MySQL解压版安装步骤

百度网盘有安装版、解压包安装包以及visual插件 链接&#xff1a;https://pan.baidu.com/s/1XXvWa40FYX5mtqofW_knIg 提取码&#xff1a;ky2q 下载地址&#xff1a;https://downloads.mysql.com/archives/community/ 解压压缩包&#xff0c;进入bin目录&#xff0c;地址栏输…

从C出发 26 --- 指针 : 一种特殊的变量

指针是变量&#xff0c; 是特殊的变量 在计算机内部逻辑上是一个一个存储单元&#xff0c;每个存储单元是一个字节 8 G /16 G 表示的是存储单元的数量 如果要确定某一个具体的存储单元&#xff0c;要怎么办&#xff1f; 可以编号&#xff0c;这里的 0 1 2 3 指的就是内存地…