【每日算法】一題五解:找「兩條鏈表的第一個公共節點」|Python 主題月

本文正在參加「Python主題月」,詳情查看 活動連接html

題目描述

這是 LeetCode 上的 劍指 Offer 52. 兩個鏈表的第一個公共節點 ,難度爲 簡單node

Tag : 「鏈表」git

輸入兩個鏈表,找出它們的第一個公共節點。github

以下面的兩個鏈表:markdown

在節點 c1 開始相交。數據結構

示例 1: app

輸入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3

輸出:Reference of the node with value = 8

輸入解釋:相交節點的值爲 8 (注意,若是兩個列表相交則不能爲 0)。從各自的表頭開始算起,鏈表 A 爲 [4,1,8,4,5],鏈表 B 爲 [5,0,1,8,4,5]。在 A 中,相交節點前有 2 個節點;在 B 中,相交節點前有 3 個節點。
複製代碼

示例 2: oop

輸入:intersectVal = 2, listA = [0,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1

輸出:Reference of the node with value = 2

輸入解釋:相交節點的值爲 2 (注意,若是兩個列表相交則不能爲 0)。從各自的表頭開始算起,鏈表 A 爲 [0,9,1,2,4],鏈表 B 爲 [3,2,4]。在 A 中,相交節點前有 3 個節點;在 B 中,相交節點前有 1 個節點。
複製代碼

示例 3: post

輸入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2

輸出:null

輸入解釋:從各自的表頭開始算起,鏈表 A 爲 [2,6,4],鏈表 B 爲 [1,5]。因爲這兩個鏈表不相交,因此 intersectVal 必須爲 0,而 skipA 和 skipB 能夠是任意值。

解釋:這兩個鏈表不相交,所以返回 null。
複製代碼

注意:ui

  • 若是兩個鏈表沒有交點,返回 null.
  • 在返回結果後,兩個鏈表仍須保持原有的結構。
  • 可假定整個鏈表結構中沒有循環。
  • 程序儘可能知足 O ( n ) O(n) 時間複雜度,且僅用 O ( 1 ) O(1) 內存。

樸素解法

一個樸素的解法天然是兩層枚舉,逐個檢查哪一個節點相同。

image.png

Java 代碼:

public class Solution {
    public ListNode getIntersectionNode(ListNode a, ListNode b) {
        for (ListNode h1 = a; h1 != null ; h1 = h1.next) {
            for (ListNode h2 = b; h2 != null ; h2 = h2.next) {
                if (h1 == h2) return h1;
            }
        }
        return null;
    }
}
複製代碼

Python 3 代碼:

class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
        h1 = headA
        while h1:
            h2 = headB
            while h2:
                if h1 == h2:
                    return h1
                h2 = h2.next
            h1 = h1.next
複製代碼
  • 時間複雜度: O ( n m ) O(n * m)
  • 空間複雜度: O ( 1 ) O(1)

棧解法

這是一種「從後往前」找的方式。

將兩條鏈表分別壓入兩個棧中,而後循環比較兩個棧的棧頂元素,同時記錄上一位棧頂元素。

當遇到第一個不一樣的節點時,結束循環,上一位棧頂元素便是答案。

image.png

Java 代碼:

public class Solution {
    public ListNode getIntersectionNode(ListNode a, ListNode b) {
        Deque<ListNode> d1 = new ArrayDeque<>(), d2 = new ArrayDeque<>();
        while (a != null) {
            d1.add(a);
            a = a.next;
        }
        while (b != null) {
            d2.add(b);
            b = b.next;
        }
        ListNode ans = null;
        while (!d1.isEmpty() && !d2.isEmpty() && d1.peekLast() == d2.peekLast()) {
            ans = d1.pollLast();
            d2.pollLast();
        }
        return ans;
    }
}
複製代碼

Python 3 代碼:

class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
        d1, d2 = deque([]), deque([])
        while headA:
            d1.append(headA)
            headA = headA.next
        while headB:
            d2.append(headB)
            headB = headB.next
        ans = None
        while d1 and d2 and d1[-1] == d2[-1]:
            ans = d1.pop()
            d2.pop()
        return ans
複製代碼
  • 時間複雜度: O ( n + m ) O(n + m)
  • 空間複雜度: O ( n + m ) O(n + m)

Set 解法

這是一種「從前日後」找的方式。

使用 Set 數據結構,先對某一條鏈表進行遍歷,同時記錄下來全部的節點。

而後在對第二鏈條進行遍歷時,檢查當前節點是否在 Set 中出現過,第一個在 Set 出現過的節點便是交點。

image.png

Java 代碼:

public class Solution {
    public ListNode getIntersectionNode(ListNode a, ListNode b) {
        Set<ListNode> set = new HashSet<>();
        while (a != null) {
            set.add(a);
            a = a.next;
        }
        while (b != null && !set.contains(b)) b = b.next;
        return b;
    }
}
複製代碼

Python 3 代碼:

class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
        hashSet = set()
        while headA:
            hashSet.add(headA)
            headA = headA.next
        while headB and headB not in hashSet:
            headB = headB.next
        return headB
複製代碼
  • 時間複雜度: O ( n + m ) O(n + m)
  • 空間複雜度: O ( n ) O(n)

差值法

因爲兩條鏈表在相交節點後面的部分徹底相同,所以咱們能夠先對兩條鏈表進行遍歷,分別獲得兩條鏈表的長度,並計算差值 d

讓長度較長的鏈表先走 d 步,而後兩條鏈表同時走,第一個相同的節點便是節點。

image.png

Java 代碼:

public class Solution {
    public ListNode getIntersectionNode(ListNode a, ListNode b) {
        int c1 = 0, c2 = 0;
        ListNode ta = a, tb = b;
        while (ta != null && c1++ >= 0) ta = ta.next;
        while (tb != null && c2++ >= 0) tb = tb.next;
        int d = c1 - c2;
        if (d > 0) {
            while (d-- > 0) a = a.next;
        } else if (d < 0) {
            d = -d;
            while (d-- > 0) b = b.next;
        }
        while (a != b) {
            a = a.next;
            b = b.next;
        }
        return a;
    }
}
複製代碼

Python 3 代碼:

class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
        c1 = c2 = 0
        ta, tb = headA, headB
        while ta:
            ta = ta.next
            c1 += 1
        while tb:
            tb = tb.next
            c2 += 1
        d = c1 - c2
        if d > 0:
            while d:
                headA = headA.next
                d -= 1
        elif d < 0:
            d = -d
            while d:
                headB = headB.next
                d -= 1
        while headA != headB:
            headA = headA.next
            headB = headB.next
        return headA
複製代碼
  • 時間複雜度: O ( n + m ) O(n + m)
  • 空間複雜度: O ( 1 ) O(1)

等值法

這是「差值法」的另一種實現形式,原理一樣利用「兩條鏈表在相交節點後面的部分徹底相同」。

咱們令第一條鏈表相交節點以前的長度爲 a,第二條鏈表相交節點以前的長度爲 b,相交節點後的公共長度爲 c(注意 c 可能爲 0 0 ,即不存在相交節點)。

分別對兩條鏈表進行遍歷:

  • 當第一條鏈表遍歷完,移動到第二條鏈表的頭部進行遍歷;
  • 當第二條鏈表遍歷完,移動到第一條鏈表的頭部進行遍歷。

若是存在交點:第一條鏈表首次到達「第一個相交節點」的充要條件是第一條鏈表走了 a + c + b a + c + b 步,因爲兩條鏈表同時出發,而且步長相等,所以當第一條鏈表走了 a + c + b a + c + b 步時,第二條鏈表一樣也是走了 a + c + b a + c + b 步,即 第二條一樣停在「第一個相交節點」的位置。

若是不存在交點:二者會在走完 a + c + b + c a + c + b + c 以後同時變爲 n u l l null ,退出循環。

image.png

Java 代碼:

public class Solution {
    public ListNode getIntersectionNode(ListNode a, ListNode b) {
        ListNode ta = a, tb = b;
        while (ta != tb) {
            ta = ta == null ? b : ta.next;
            tb = tb == null ? a : tb.next;
        }
        return ta;
    }
}
複製代碼

Python 3 代碼:

class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
        ta, tb = headA, headB
        while ta != tb:
            ta = headB if not ta else ta.next
            tb = headA if not tb else tb.next
        return ta
複製代碼
  • 時間複雜度: O ( n + m ) O(n + m)
  • 空間複雜度: O ( 1 ) O(1)

最後

這是咱們「刷穿 LeetCode」系列文章的第 No.劍指 Offer 52 篇,系列開始於 2021/01/01,截止於起始日 LeetCode 上共有 1916 道題目,部分是有鎖題,咱們將先把全部不帶鎖的題目刷完。

在這個系列文章裏面,除了講解解題思路之外,還會盡量給出最爲簡潔的代碼。若是涉及通解還會相應的代碼模板。

爲了方便各位同窗可以電腦上進行調試和提交代碼,我創建了相關的倉庫:github.com/SharingSour…

在倉庫地址裏,你能夠看到系列文章的題解連接、系列文章的相應代碼、LeetCode 原題連接和其餘優選題解。

相關文章
相關標籤/搜索