Leetcode 14 java

今天復習一下以前做過的題目,感覺是忘光了。

160. 相交鏈表

給你兩個單鏈表的頭節點?headA?和?headB?,請你找出并返回兩個單鏈表相交的起始節點。如果兩個鏈表不存在相交節點,返回?null?。

圖示兩個鏈表在節點?c1?開始相交

題目數據?保證?整個鏈式結構中不存在環。

注意,函數返回結果后,鏈表必須?保持其原始結構?。

自定義評測:

評測系統?的輸入如下(你設計的程序?不適用?此輸入):

  • intersectVal?- 相交的起始節點的值。如果不存在相交節點,這一值為?0
  • listA?- 第一個鏈表
  • listB?- 第二個鏈表
  • skipA?- 在?listA?中(從頭節點開始)跳到交叉節點的節點數
  • skipB?- 在?listB?中(從頭節點開始)跳到交叉節點的節點數

評測系統將根據這些輸入創建鏈式數據結構,并將兩個頭節點?headA?和?headB?傳遞給你的程序。如果程序能夠正確返回相交節點,那么你的解決方案將被?視作正確答案?。

示例 1:

輸入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,6,1,8,4,5], skipA = 2, skipB = 3
輸出:Intersected at '8'
解釋:相交節點的值為 8 (注意,如果兩個鏈表相交則不能為 0)。
從各自的表頭開始算起,鏈表 A 為 [4,1,8,4,5],鏈表 B 為 [5,6,1,8,4,5]。
在 A 中,相交節點前有 2 個節點;在 B 中,相交節點前有 3 個節點。
— 請注意相交節點的值不為 1,因為在鏈表 A 和鏈表 B 之中值為 1 的節點 (A 中第二個節點和 B 中第三個節點) 是不同的節點。換句話說,它們在內存中指向兩個不同的位置,而鏈表 A 和鏈表 B 中值為 8 的節點 (A 中第三個節點,B 中第四個節點) 在內存中指向相同的位置。

示例?2:

輸入:intersectVal?= 2, listA = [1,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
輸出:Intersected at '2'
解釋:相交節點的值為 2 (注意,如果兩個鏈表相交則不能為 0)。
從各自的表頭開始算起,鏈表 A 為 [1,9,1,2,4],鏈表 B 為 [3,2,4]。
在 A 中,相交節點前有 3 個節點;在 B 中,相交節點前有 1 個節點。

示例?3:

輸入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2
輸出:No intersection
解釋:從各自的表頭開始算起,鏈表 A 為 [2,6,4],鏈表 B 為 [1,5]。
由于這兩個鏈表不相交,所以 intersectVal 必須為 0,而 skipA 和 skipB 可以是任意值。
這兩個鏈表不相交,因此返回 null 。

提示:

  • listA?中節點數目為?m
  • listB?中節點數目為?n
  • 1 <= m, n <= 3 * 104
  • 1 <= Node.val <= 105
  • 0 <= skipA <= m
  • 0 <= skipB <= n
  • 如果?listA?和?listB?沒有交點,intersectVal?為?0
  • 如果?listA?和?listB?有交點,intersectVal == listA[skipA] == listB[skipB]

進階:你能否設計一個時間復雜度?O(m + n)?、僅用?O(1)?內存的解決方案?

1 我的想法以及分析

看到這個題目我的反應是要用指針的,之前做過是有一點點印象的(但不多)。

1.如果有交點

那么從尾指針開始回溯,兩個鏈表到從尾到交點的節點數一定是相同的

2.如果沒有交點

那么從尾指針開始回溯,兩個鏈表最終指針會回到頭指針,節點數就是鏈表的長度(沒有前驅指針,這個不能實現)

那這個想法有什么用呢?如果我們存在一個這樣的指針:兩個鏈表都從尾開始遍歷,遇到交叉節點停下并返回當前所在節點位置,如果沒有遇到就返回鏈表長度(所以就算沒有交叉)

這就是我的思路了,但是實際上我應該怎么操作?

Listnode Pa = headA ,Pb =headB;//題目里只給了默認是頭指針

其實我可以把原來的思路改一下,每個指針在交叉節點處停下,如果余下的鏈表長度一致,說明當前節點是交叉節點,如果不是,那就是沒有相交(按照題目所給的數據)

往下讀取的操作是

Pa=Pa.next

怎么讀取第一個鏈表的節點呢?

不知道怎么寫了,是不是有一個統一長度的操作?(看題解了hh)把鏈表全部向右對齊,左邊空的地方用null填滿。

while(Pa ! = Pb){

Pa = null ? headB : null ;

Pb = null ? headA : null ;

}? //這里回頭看發現寫的是錯的!!!!!就留著這個錯誤吧長記性。

return Pa;//看懂了,如果對齊了發覺沒有相同節點,那么就是沒有相交。

還有值得注意的是,如果一開始給的就是兩個空鏈表,那肯定就是null

if (headA = null || headB = null )

{

return null;
}

/*** 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;ListNode pA = headA , pB = headB ;while (pA != pB){pA = pA == null ? headB : pA.next;pB = pB == null ? headA : pB.next;}return pA;}
}


?

你的思路方向和對題目的理解是準確且有價值的,尤其抓住了 “相交鏈表從交點到尾端長度一致” 的核心特征,這是解決問題的關鍵前提,說明你對鏈表相交的本質邏輯有清晰認知。

同時,你也注意到了兩個重要細節:

  1. 想到了 “統一長度” 的操作(后續結合題解理解的 “對齊” 思路),這和雙指針法的核心邏輯不謀而合;
  2. 考慮到 “空鏈表直接返回 null” 的邊界情況,避免了異常場景的遺漏。

如果說可以優化的點,主要是代碼實現的細節銜接:

  • 最初思路里 “從尾回溯” 的操作,在單鏈表中無法直接實現(單鏈表只能從前向后遍歷,沒有前驅指針),所以后續轉向 “雙指針切換鏈表” 的思路是非常正確的調整;
  • 代碼里 “Pa = null ? headB : null ;” 這類語法需要修正(三元運算符的條件和結果寫反了),但你能理解 “指針為空時切換到另一鏈表頭部” 的邏輯,已經掌握了算法的核心。

2 分析

要解決相交鏈表問題,核心是利用雙指針 “對齊” 鏈表長度的思路,在滿足時間復雜度 O (m+n) 和空間復雜度 O (1) 的前提下找到相交節點。以下是完整分析和代碼實現:

一、核心思路:雙指針 “路程補償” 法

兩個鏈表若相交,從相交節點到尾節點的 “共同段長度” 是相同的。差異在于相交前的 “獨有段長度”(設為lenA獨lenB獨)。
雙指針通過遍歷完自身鏈表后切換到另一鏈表頭部,實現 “路程補償”:

  • 指針paheadA出發,遍歷完 A 后切換到headB繼續走;
  • 指針pbheadB出發,遍歷完 B 后切換到headA繼續走;
  • 最終兩指針會 “對齊” 到相交節點(若相交),或同時走到null(若不相交)。

原理:兩指針最終走的總路程都是?lenA獨 + lenB獨 + 共同段長度,路程相等且速度相同,必然在目標位置相遇。

二、代碼實現(含邊界處理)

java

運行

/*** 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) {// 邊界處理:若任一鏈表為空,直接返回null(無相交可能)if (headA == null || headB == null) {return null;}ListNode pa = headA; // 指針pa初始指向A的頭ListNode pb = headB; // 指針pb初始指向B的頭// 循環終止條件:pa == pb(要么是相交節點,要么是都為null)while (pa != pb) {// pa走一步:若遍歷完A,切換到B的頭;否則走nextpa = (pa == null) ? headB : pa.next;// pb走一步:若遍歷完B,切換到A的頭;否則走nextpb = (pb == null) ? headA : pb.next;}// 最終pa和pb要么同時指向相交節點,要么同時為nullreturn pa;}
}

三、關鍵細節解釋

  1. 邊界處理:若headAheadB為空,直接返回null(空鏈表不可能與其他鏈表相交)。
  2. 循環邏輯
    • pa遍歷完 A(即pa == null),切換到 B 的頭部繼續走,相當于 “補償”lenB獨的長度;
    • pb遍歷完 B(即pb == null),切換到 A 的頭部繼續走,相當于 “補償”lenA獨的長度;
    • 若兩鏈表相交:papb會在遍歷完 “自身長度 + 對方獨有長度” 后,同時到達相交節點;
    • 若兩鏈表不相交:papb會在遍歷完 “自身長度 + 對方長度” 后,同時到達null
  3. 原始結構保留:僅通過指針遍歷,未修改鏈表的next指向,滿足題目要求。

四、示例驗證(以示例 1 為例)

  • 鏈表 A:4 → 1 → 8 → 4 → 5(長度 5);鏈表 B:5 → 6 → 1 → 8 → 4 → 5(長度 6)。
  • pa的路徑:4→1→8→4→5→null→5→6→1→8(第 9 步到相交節點 8);
  • pb的路徑:5→6→1→8→4→5→null→4→1→8(第 9 步到相交節點 8);
  • 最終pa == pb(都指向 8),返回該節點,符合預期

題友說:

“關于第二種解法發表下我的見解,統一長度說白了就是為了兩個鏈表向右對齊,打個比方listA長度為5,listB長度為6,不好比較,那就把前面補充成null(先這樣想)
listA=[null,4,1,8,4,5] listB=[5,6,1,8,4,5]
那這樣長度就一樣了,我們就能同時從開頭移動A和B進行比較。
那回到正常的思路,想A和B長度一樣,長度就都設置為A+B唄。那就往B最左邊補充A長度(5)個null,A最左邊補充B長度(6)個null。那就變成
listA=[null,null,null,null,null,null,4,1,8,4,5] listB=[null,null,null,null,null,5,6,1,8,4,5]
這樣長度一樣,好比較了吧,不過都是null,鏈表怎么移動啊,你就把A里面的6個null換成B的數據,B里面的null換成A的數據唄,反正都是要白白移動的,相交點在最后面,這樣補全是為了我們好操作鏈表。
那這樣理解的話這題就沒問題了。代碼是官方第二種解法的代碼。”

3 (未完待續)哈希集合解法:相交鏈表問題的另一種思路

這個解法還沒嘗試過。

除了之前討論的雙指針法,哈希集合也是解決相交鏈表問題的經典思路,其核心是通過存儲一個鏈表的所有節點,再遍歷另一個鏈表查找 “共同節點”,具體分析如下:

一、思路與算法邏輯

哈希集合的核心是利用 “集合元素唯一性” 的特性,將一個鏈表的節點全部存入集合后,通過檢查另一個鏈表的節點是否在集合中,判斷是否相交及找到相交節點。步驟如下:

  1. 存儲鏈表 A 的節點:遍歷鏈表headA,將每個節點(注意是存儲節點本身,而非節點值)加入哈希集合visited
  2. 查找鏈表 B 的節點:遍歷鏈表headB,對每個節點執行判斷:
    • 若該節點在visited中:說明此節點是兩個鏈表的 “第一個共同節點”(因為是從headB頭開始遍歷),直接返回該節點。
    • 若遍歷完headB所有節點都不在visited中:說明兩鏈表不相交,返回null

二、代碼實現(Java)

java

運行

import java.util.HashSet;
import java.util.Set;/*** 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) {// 哈希集合:存儲鏈表A的所有節點Set<ListNode> visited = new HashSet<ListNode>();ListNode temp = headA;// 第一步:遍歷鏈表A,將所有節點加入集合while (temp != null) {visited.add(temp);temp = temp.next;}// 第二步:遍歷鏈表B,查找集合中存在的節點temp = headB;while (temp != null) {if (visited.contains(temp)) {return temp; // 找到第一個相交節點,直接返回}temp = temp.next;}// 遍歷完B仍未找到,說明無相交return null;}
}

三、關鍵細節說明

  1. 存儲 “節點” 而非 “節點值”
    題目中明確 “值相同的節點可能是不同對象”(如示例 1 中 A 的 “1” 和 B 的 “1” 是不同節點),因此必須存儲節點的引用(地址),而非僅比較值,避免誤判。
  2. “第一個共同節點” 的必然性
    遍歷headB時,第一個在集合中找到的節點就是相交起點 —— 因為headB從頭部開始遍歷,且相交后的所有節點都在集合中,第一個匹配的節點即為最早的相交節點。
  3. 原始結構保留
    僅通過指針遍歷和集合存儲,未修改任何鏈表的next指向,滿足題目 “保持原始結構” 的要求。

四、復雜度分析

維度分析
時間復雜度O (m + n):m 為headA長度,n 為headB長度,需各遍歷一次,無嵌套循環。
空間復雜度O (m):需存儲headA的所有 m 個節點,空間開銷隨headA長度線性增長。

五、與雙指針法的對比

對比維度哈希集合法雙指針法
時間復雜度O (m + n)(相同)O (m + n)(相同)
空間復雜度O (m)(需額外存儲一個鏈表)O (1)(僅用兩個指針,最優)
實現難度邏輯直觀,易理解需理解 “路程補償” 邏輯

若題目對空間開銷無嚴格限制,哈希集合法是更易想到的解法;若要求O (1) 空間(如進階要求),則雙指針法更優。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/95776.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/95776.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/95776.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

用 FreeMarker 動態構造 SQL 實現數據透視分析

在 ERP、BI 等系統中&#xff0c;數據透視分析&#xff08;Pivot Analysis&#xff09;是非常常見的需求&#xff1a;用戶希望按任意維度&#xff08;如門店、時間、商品分類等&#xff09;進行分組統計&#xff0c;同時選擇不同的指標&#xff08;如 GMV、訂單數、客單價等&am…

13.深度學習——Minst手寫數字識別

第一部分——起手式 import torch from torchvision import datasets, transforms import torch.nn as nn import torch.nn.functional as F import torch.optim as optimuse_cuda torch.cuda.is_available()if use_cuda:device torch.device("cuda") else: device…

【JAVA高級】實現word轉pdf 實現,源碼概述。深坑總結

之前的需求做好后,需求,客戶突發奇想。要將生成的word轉為pdf! 因為不想讓下載文檔的人改動文檔。 【JAVA】實現word添加標簽實現系統自動填入字段-CSDN博客 事實上這個需求難度較高,并不是直接轉換就行的 word文檔當中的很多東西都需要處理 public static byte[] gener…

數據驅動測試提升自動化效率

測試工程師老王盯著滿屏重復代碼嘆氣&#xff1a;“改個搜索條件要重寫20個腳本&#xff0c;這班加到啥時候是個頭&#xff1f;” 隔壁組的小李探過頭&#xff1a;“試試數據驅動唄&#xff0c;一套腳本吃遍所有數據&#xff0c;我們組上周測了300個組合都沒加班&#xff01;”…

模板引用(Template Refs)全解析2

三、v-for 中的模板引用 當在 v-for 中使用模板引用時,引用的 value 會自動變為一個數組,包含列表中所有元素/組件的引用(需 Vue 3.5+ 版本,舊版需手動處理且順序不保證)。 1. 基本用法(Vue 3.5+) <script setup> import { ref, useTemplateRef, onMounted } f…

【Linux系統】進程間通信:System V IPC——共享內存

前文中我們介紹了管道——匿名管道和命名管道來實現進程間通信&#xff0c;在介紹怎么進行通信時&#xff0c;我們有提到過不止管道的方式進行通信&#xff0c;還有System V IPC&#xff0c;今天這篇文章我們就來學習一下System V IPC中的共享內存1. 為何引入共享內存&#xff…

[優選算法專題二滑動窗口——最大連續1的個數 III]

題目鏈接 最大連續1的個數 III 題目描述 題目解析 問題本質 輸入&#xff1a;二進制數組nums&#xff08;只包含 0 和 1&#xff09;和整數k操作&#xff1a;最多可以將k個 0 翻轉成 1目標&#xff1a;找到翻轉后能得到的最長連續 1 的子數組長度 這個問題的核心是要找到一…

C#單元測試(xUnit + Moq + coverlet.collector)

C#單元測試 xUnit Moq coverlet.collector 1.添加庫 MlyMathLib 2.編寫庫函數內容 using System;namespace MlyMathLib {public interface IUserRepo{string GetName(int id);}public class UserService{private readonly IUserRepo _repo;public UserService(IUserRepo repo…

【數據庫】Oracle學習筆記整理之五:ORACLE體系結構 - 參數文件與控制文件(Parameter Files Control Files)

Oracle體系結構 - 參數文件與控制文件&#xff08;Parameter Files & Control Files&#xff09; 參數文件與控制文件是Oracle數據庫的“雙核基石”&#xff1a;參數文件是實例的“啟動配置中心”&#xff0c;定義運行環境與規則&#xff1b;控制文件是數據庫的“物理元數據…

GDB典型開發場景深度解析

GDB典型開發場景深度解析 以下是開發過程中最常見的GDB使用場景&#xff0c;結合具體實例和調試技巧&#xff0c;幫助開發者高效解決實際問題&#xff1a;一、崩潰分析&#xff08;Core Dump調試&#xff09; 場景&#xff1a;程序突然崩潰&#xff0c;生成了core文件 # 啟動調…

存儲、硬盤、文件系統、 IO相關常識總結

目錄 &#xff08;一&#xff09;存儲 &#xff08;1&#xff09;定義 &#xff08;2&#xff09;分類 &#xff08;二&#xff09;硬盤 &#xff08;1&#xff09;容量&#xff08;最主要的參數&#xff09; &#xff08;2&#xff09;轉速 &#xff08;3&#xff09;訪…

docker安裝mongodb及java連接實戰

1.docker部署mongodb docker run --name mongodb -d -p 27017:27017 -v /data/mongodbdata:/data/db -e MONGO_INITDB_ROOT_USERNAMEtestmongo -e MONGO_INITDB_ROOT_PASSWORDtest123456 mongodb:4.0.112.項目實戰 <dependencies><dependency><groupId>org.m…

Java設計模式之《工廠模式》

目錄 1、介紹 1.1、定義 1.2、優缺點 1.3、使用場景 2、實現 2.1、簡單工廠模式 2.2、工廠方法模式 2.3、抽象工廠模式 3、小結 前言 在面向對象編程中&#xff0c;創建對象實例最常用的方式就是通過 new 操作符構造一個對象實例&#xff0c;但在某些情況下&#xff0…

【異步】js中異步的實現方式 async await /Promise / Generator

JS的異步相關知識 js里面一共有以下異步的解決方案 傳統的回調 省略 。。。。 生成器 Generator 函數是 ES6 提供的一種異步編程解決方案, 語法上&#xff0c;首先可以把它理解成&#xff0c;Generator 函數是一個狀態機&#xff0c;封裝了多個內部狀態。執行 Generator 函數…

JVM字節碼文件結構

Class文件結構class文件是二進制文件&#xff0c;這里要介紹的是這個二級制文件的結構。思考&#xff1a;一個java文件編譯成class文件&#xff0c;如果要描述一個java文件&#xff0c;需要哪些信息呢&#xff1f;基本信息&#xff1a;類名、父類、實現哪些接口、方法個數、每個…

11.web api 2

5. 操作元素屬性 5.1操作元素常用屬性 &#xff1a;通過 JS 設置/修改標簽元素屬性&#xff0c;比如通過 src更換 圖片最常見的屬性比如&#xff1a; href、title、src 等5.2 操作元素樣式屬性 &#xff1a;通過 JS 設置/修改標簽元素的樣式屬性。使用 className 有什么好處&a…

java中數組和list的區別是什么?

在Java中&#xff0c;數組&#xff08;Array&#xff09;和List&#xff08;通常指java.util.List接口的實現類&#xff0c;如ArrayList、LinkedList&#xff09;是兩種常用的容器&#xff0c;但它們在設計、功能和使用場景上有顯著區別。以下從核心特性、使用方式等方面詳細對…

Python爬取推特(X)的各種數據

&#x1f31f; Hello&#xff0c;我是蔣星熠Jaxonic&#xff01; &#x1f308; 在浩瀚無垠的技術宇宙中&#xff0c;我是一名執著的星際旅人&#xff0c;用代碼繪制探索的軌跡。 &#x1f680; 每一個算法都是我點燃的推進器&#xff0c;每一行代碼都是我航行的星圖。 &#x…

Oracle數據庫文件管理與空間問題解決指南

在Oracle數據庫運維中&#xff0c;表空間、數據文件及相關日志文件的管理是保障數據庫穩定運行的核心環節。本文將系統梳理表空間與數據文件的調整、關鍵文件的移動、自動擴展配置&#xff0c;以及常見空間不足錯誤的排查解決方法&#xff0c;為數據庫管理員提供全面參考。 一、…

華為實驗綜合小練習

描述&#xff1a; 1 內網有A、B、C 三個部門。所在網段如圖所示。 2 內網服務器配置靜態IP,網關192.168.100.1。 3 sw1和R1之間使用vlan200 192.168.200.0/30 互聯。 4 R1向運營商申請企業寬帶并申請了5個公網IP&#xff1a;200.1.1.1-.5子網掩碼 255.255.255.248&#xff0c;網…