LeetCode Java两个单链表相交的一系列问题

news2024/11/13 13:08:08

题目描述

单链表可能有环,也可能无环。给定两个单链表的头节点 head1和head2,这两个链表可能相交,也可能不相交。

请实现一个函数,如果两个链表相交,请返回相交的第一个节点;如果不相交,返回null 即可。

要求:如果链表1的长度为N,链表2的长度为M,时间复杂度请达到 O(N+M),额外空间复杂度请达到O(1)。

问题思考

如何获取单链表对应的入环节点;

单链表是否有环
处理思路:采用快慢指针方式,快指针每次走两步,慢指针每次走一步,如果遍历结束也不相遇,则此链表无环,当两者相遇时,移动快指针到链表头,然后快慢指针每次走一步,再次相遇,则此节点必是入环节点;

针对单链表是否有环相交,需要考虑以下几种情况;

1. 两个链表都没有环的情况,可能存在相交节点;链表结构类似如下:链表均无环
处理思路:如果两个链表长度一致,则直接遍历链表,找到同一个节点返回即可,如果长度不一致,则需要先对齐链表长度,然后再遍历链表,找到同一个节点返回;

  1. 两个链表一个有环,一个无环的情况,这种情况必然不可能相交,若相交,则必然都有环;
    一个有环,一个无环
    3.两个链表都有环,则可能出现以下两种情况:
    • 入环节点是同一节点;
      入环节点是同一节点
      处理思路:若环前节点数不相同,则需要先对齐环前链表长度,然后和1中相同方式处理即可;

    • 入环节点是不同节点;
      入环节点是不同节点
      处理思路:从head1环节点开始遍历环,当节点等于head2环节点时,返回即可;

代码实现

public class FindFirstIntersectNode {

    public static class Node {
        public int value;
        public Node next;

        public Node(int value) {
            this.value = value;
        }
    }


    public static Node getIntersectNode(Node head1, Node head2) {
        if (head1 == null || head2 == null) {
            return null;
        }
        //分别获取head1、head2对应的入环节点
        Node loop1 = getLoopNode(head1);
        Node loop2 = getLoopNode(head2);
        //两个单链表一个有环一个无环的时候不可能相交
        //两个单链表都有环,则也不可能相交【不满足单链表条件】
        if (loop1 == null && loop2 == null) {
            return noLoop(head1, head2);
        }
        if (loop1 != null && loop2 != null) {
            return bothLoop(head1, loop1, head2, loop2);
        }
        return null;
    }

    /**
     * 两个链表都是有环链表【需要考虑入环节点是不是同一节点问题】
     *
     * @param head1
     * @param loop1
     * @param head2
     * @param loop2
     * @return
     */
    private static Node bothLoop(Node head1, Node loop1, Node head2, Node loop2) {
        Node cur1 = null;
        Node cur2 = null;
        if (loop1 == loop2) {
            //入环节点为同一节点,和处理无环方式基本类似
            cur1 = head1;
            cur2 = head2;
            int n = 0;
            //对齐入环前节点差值
            while (cur1 != loop1) {
                n++;
                cur1 = cur1.next;
            }
            while (cur2 != loop2) {
                n--;
                cur2 = cur2.next;
            }
            cur1 = n > 0 ? head1 : head2;
            cur2 = cur1 == head1 ? head2 : head1;
            n = Math.abs(n);
            while (n != 0) {
                n--;
                cur1 = cur1.next;
            }
            while (cur1 != cur2) {
                cur1 = cur1.next;
                cur2 = cur2.next;
            }
            return cur1;
        } else {
            //入环节点不是同一个节点,从入环节点loop1开始,转一圈看能不能遇到loop2.
            cur1 = loop1.next;
            while (cur1 != loop1) {
                if (cur1 == loop2) {
                    return loop1;
                }
                cur1 = cur1.next;
            }
            return null;
        }
    }

    /**
     */
    private static Node noLoop(Node head1, Node head2) {
        if (head1 == null || head2 == null) {
            return null;
        }
        Node cur1 = head1;
        Node cur2 = head2;
        int n = 0;
        while (cur1.next != null) {
            n++;
            cur1 = cur1.next;
        }
        while (cur2.next != null) {
            n--;
            cur2 = cur2.next;
        }
        //如果最终不是落在同一个节点上,则无交点
        if (cur1 != cur2) {
            return null;
        }
        //n为两个链表长度的差值
        //cur1取长度长的那个  cur2取另外一个
        cur1 = n > 0 ? head1 : head2;
        cur2 = cur1 == head1 ? head2 : head1;
        n = Math.abs(n);
        while (n != 0) {
            //将二者长度调整到一致
            n--;
            cur1 = cur1.next;
        }
        while (cur1 != cur2) {
            //同步遍历,相等时即为相交节点
            cur1 = cur1.next;
            cur2 = cur2.next;
        }
        return cur1;
    }


    /**
     * 获取对应链表的入环节
     *
     * @param head
     * @return
     */
    private static Node getLoopNode(Node head) {
        //2个节点无法形成环
        if (head == null || head.next == null || head.next.next == null) {
            return null;
        }
        //定义快慢指针
        Node slow = head.next;
        Node fast = head.next.next;
        while (slow != fast) {
            //移动结束 快慢指针也不相等,则一定无环
            if (fast.next == null || fast.next.next == null) {
                return null;
            }
            slow = slow.next;
            fast = fast.next.next;
        }
        //上面遍历结束,slow == fast 上述例子中会相遇,移动fast指针到头结点处
        fast = head;
        //再次移动快慢指针,则再次相遇节点必为入环节点
        while (slow != fast) {
            slow = slow.next;
            fast = fast.next;
        }
        return slow;

    }


    public static void main(String[] args) {
        // 1->2->3->4->5->6->7->null
        Node head1 = new Node(1);
        head1.next = new Node(2);
        head1.next.next = new Node(3);
        head1.next.next.next = new Node(4);
        head1.next.next.next.next = new Node(5);
        head1.next.next.next.next.next = new Node(6);
        head1.next.next.next.next.next.next = new Node(7);

        // 0->9->8->6->7->null
        Node head2 = new Node(0);
        head2.next = new Node(9);
        head2.next.next = new Node(8);
        head2.next.next.next = head1.next.next.next.next.next; // 8->6
        System.out.println(getIntersectNode(head1, head2).value);

        // 1->2->3->4->5->6->7->4...
        head1 = new Node(1);
        head1.next = new Node(2);
        head1.next.next = new Node(3);
        head1.next.next.next = new Node(4);
        head1.next.next.next.next = new Node(5);
        head1.next.next.next.next.next = new Node(6);
        head1.next.next.next.next.next.next = new Node(7);
        head1.next.next.next.next.next.next = head1.next.next.next; // 7->4

        // 0->9->8->2...
        head2 = new Node(0);
        head2.next = new Node(9);
        head2.next.next = new Node(8);
        head2.next.next.next = head1.next; // 8->2
        System.out.println(getIntersectNode(head1, head2).value);

        // 0->9->8->6->4->5->6..
        head2 = new Node(0);
        head2.next = new Node(9);
        head2.next.next = new Node(8);
        head2.next.next.next = head1.next.next.next.next.next; // 8->6
        System.out.println(getIntersectNode(head1, head2).value);

    }
}

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

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

相关文章

Android 渐变背景色

目录 一、背景 二、渐变 2.1 线性渐变背景色 1.新建资源文件 2.编辑样式文件 3.使用 4.编辑样式参数说明 2.2 圆角按钮渐变背景色 2.3 放射渐变 2.4 扫描线渐变 一、背景 单纯的颜色背景已经不能够满足UI大佬们的发挥,渐变色背景无疑成了一个炫技的方向。现在…

chatgpt赋能python:Python调用同一个类中方法详解

Python调用同一个类中方法详解 在Python编程中,类是一种非常重要的概念,可用于组织和管理代码。在同一个类中,可以定义多个方法。本文将详细介绍如何调用同一个类中的方法。 什么是类方法? 在Python中,类方法是指类…

魔兽世界自己架设任务

在魔兽世界中,玩家可以使用游戏内的任务编辑器自己架设任务来增加游戏的乐趣和挑战性。以下是详细的步骤: 第一步:打开任务编辑器 玩家可以在游戏中按下“ESC”键,进入游戏设置页面。在这个页面中,有一个“编辑器”选…

DSL查询分类与全文检索查询

DSL查询分类 Elasticsearch提供了基于JSON的DSL(Domain Specific Language)来定义查询。常见的查询类型包括: 查询所有:查询出所有数据,一般测试用。例如:match_all全文检索(full text&#x…

Idea新建springboot项目遇到的问题及解决

1.更换阿里云 方法&#xff1a; 找到文件路径&#xff1a;Settings > Build,Execution,Deployment > Build Tools > Maven 如下图&#xff1a; 找到相应的settings文件 如果没有就新建一个同名文件&#xff0c;内容如下&#xff1a; <settings xmlns"h…

Gitlab回退到指定版本的方法与步骤

一、先根据分支获取代码 如下&#xff1a; 下载好后&#xff0c;通过右键菜单进入git bash here 就进入下面界面 去gitlab上面去寻找需要的faf0af86d24f7de73b024785ad864f36da4284e2 git reset --hard cf2a5283b9a79f8cf04b003d05cdd94b2b3ff166 执行命令“git push -f”&…

vue中对语句的语义进行比较

一、安装 string-similarity库 npm install string-similarity二、html <div><input type"text" v-model"string1" placeholder"文本1" /> </div> <div><input type"text" v-model"string2" p…

[计算机入门]了解键盘

2.1 了解键盘 键盘一般可以根据按键的功能进行分区&#xff0c;一般分为&#xff1a;主键盘区、小键盘区、控制键区、功能键区、指示灯区。下面介绍键盘的各个分区按键及功能。 2.1.1 主键盘区 主键盘区又叫打字键盘区或字符键区&#xff0c;具有标准英文打字机键盘的格式。…

【Java】Java 中的栈和堆内存

本文仅供学习参考&#xff01; 相关教程地址&#xff1a; https://www.cnblogs.com/whgw/archive/2011/09/29/2194997.html https://www.developer.com/java/stack-heap-java-memory/ https://zhuanlan.zhihu.com/p/529280783 Java 数据类型在执行过程中存储在两种不同形式的内…

HOT24-回文链表

leetcode原题链接&#xff1a;回文链表 题目描述 给你一个单链表的头节点 head &#xff0c;请你判断该链表是否为回文链表。如果是&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 示例 1&#xff1a; 输入&#xff1a;head [1,2,2,1] 输出&#xff1a…

Leetcode 刷题 动态规划

198. 打家劫舍 1. 确定dp数组&#xff08;dp table&#xff09;以及下标的含义 dp[i]&#xff1a;考虑下标i&#xff08;包括i&#xff09;以内的房屋&#xff0c;最多可以偷窃的金额为dp[i] 2. 确定递推公式 dp[i] max(dp[i - 2] nums[i], dp[i - 1]); 3. dp数组如何初始…

分类预测 | MATLAB实现GA-BiLSTM遗传算法优化双向长短期记忆网络的数据多输入分类预测

分类预测 | MATLAB实现GA-BiLSTM遗传算法优化双向长短期记忆网络的数据多输入分类预测 目录 分类预测 | MATLAB实现GA-BiLSTM遗传算法优化双向长短期记忆网络的数据多输入分类预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 MATLAB实现GA-BiLSTM遗传算法优化双向长短…

【数据库】MySQL慢查询常用分析方法

系统慢慢越来越卡了&#xff0c;怎么定位系统慢的原因&#xff0c;大部分是因为服务器资源占用耗费高引起的&#xff0c;如CPU&#xff0c;内存和带宽等等。MySQL在日常开发工作中可能会遇到某个新功能在测试时需要很久才返回结果&#xff0c;这时就应该分析是不是慢查询导致的…

【javascript】2048小游戏

目录 什么是2048 游戏状态机 游戏界面绘制 3.1 界面 3.2 数字的背景颜色 分数逻辑 4.1 加分 4.2 更新最高分 方向控制逻辑 5.1 数组 5.2 随机数 5.3 初始化 5.4 判断数组是否全部填满 5.5 判断方格是否还能移动 5.6 上下左右的监听事件 5.7 移动 完整代码 …

微服务划分的姿势

我们知道微服务是一种理念&#xff0c;没有确切的定义和边界&#xff0c;好比设计原则&#xff0c;是属于抽象的概念。在定义不明确的情况下谈划分也是一种各说各话&#xff0c;具体问题需要具体分析&#xff0c;所以这篇文章谈到的划分也不是绝对标准&#xff0c;仅供参考。 有…

final, finally和finalize的区别

final、finally和finalize是Java中用于异常处理的关键字。每个关键字都有不同的功能。final是一个访问修饰符&#xff0c;finally是异常处理中的代码块&#xff0c;而finalize是Object类的方法。 除此之外&#xff0c;final、finally和finalize之间还存在许多区别。下面是final…

netty学习(1):1个客户端与服务器通信

1. 新建maven工程&#xff0c;添加netty依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"…

Avoid adding reactive properties to a Vue instance or its root $da

避免在运行时向Vue实例或其根$data添加反应性属性-在数据选项中预先声明它。 在页面中声明对象&#xff0c;直接修改即可。 data(){return{addressInfo:{}}}

阿里云服务器地域可用区怎么选?

阿里云服务器地域和可用区怎么选择&#xff1f;地域是指云服务器所在物理数据中心的位置&#xff0c;地域选择就近选择&#xff0c;访客距离地域所在城市越近网络延迟越低&#xff0c;速度就越快&#xff1b;可用区是指同一个地域下&#xff0c;网络和电力相互独立的区域&#…

如何在Mac上安装 Stable Diffusion 来创作

​ 看着别人玩&#xff0c;是不是特想自己搭建一个&#xff0c;那么现在教程来了。 玩这种需要算力的东西&#xff0c;电脑配置肯定是越高越好了。我的电脑配置如下&#xff1a;​ 接下来就开始安装了。 第一步&#xff1a;安装homebrew 打开terminal终端&#xff08;comma…