一、题目

编写一个程序,找到两个单链表相交的起始节点。

如下面的两个链表:



在节点 c1 开始相交。

示例 1:

输入: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:

输入: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:

输入: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。

注意:

  • 如果两个链表没有交点,返回 null.
  • 在返回结果后,两个链表仍须保持原有的结构。
  • 可假定整个链表结构中没有循环。
  • 程序尽量满足 O(n) 时间复杂度,且仅用 O(1) 内存。

二、题解

  • 题解1:哈希表

遍历链表 A 并将每个节点的地址/引用存储在哈希表中。

然后检查链表 B 中的每一个节点是否在哈希表中。若在,则为相交结点。

/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func getIntersectionNode(headA, headB *ListNode) *ListNode {
var s [] *ListNode for headA != nil {
s = append(s, headA)
headA = headA.Next
}
for headB != nil {
for _,v := range(s) {
if v == headB {
return headB
}
}
headB = headB.Next
}
return nil
}
  • 题解2:链表拼接法

当链表 A 走到尾部的 null 时,转到链表 B 的头节点继续走;

当链表 B 走到尾部的 null 时,转到链表 A 的头节点继续走;

若两链表相交,则 A 和 B 一定相遇。



如上图:初始化 pA = headA, pB = headB,开始遍历。

pA会先到达链表尾,当pA到达末尾时,重置pA为headB;同样的,当pB到达末尾时,重置pB为headA。当pA与pB相遇时,必然就是两个链表的交点。

为什么要这样处理?因为对 pA 而言,走过的路程为 a+c+b,对 pB 而言,为 b+c+a,显然 a+c+b = b+c+a,这就是该算法的核心原理。

即使两个链表没有相交点,仍然可以统一处理,因为这种情况意味着相交点就是 null,也就是上图中的公共部分c没有了,从而递推式变成了 pA: a+bpB: b+a,同样是成立的。

时间复杂度:O(m+n),空间复杂度:O(1)。

/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func getIntersectionNode(headA, headB *ListNode) *ListNode {
if (headA == nil || headB == nil) {
return nil
}
pA, pB := headA, headB
for pA != pB {
if pA == nil {
pA = headB
} else {
pA = pA.Next
}
if pB == nil {
pB = headA
} else {
pB = pB.Next
}
}
return pA
}
  • 题解3:消除链表长度差

两个单链表,有公共结点,则必然尾部公用;

分别找出链表 1 和链表 2 的长度,长的链表减去短的链表得出一个 n 值;

长的链表先走 n 步,两个链表再同时移动,则两个链表相交点就是第一个公共结点。

时间复杂度:O(n),空间复杂度:O(1)。

/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func getIntersectionNode(headA, headB *ListNode) *ListNode {
if headA == nil || headB == nil {
return nil
}
lenA := getLen(headA)
lenB := getLen(headB) //计算链表长度差 n,长的先移动 n 步
if (lenA > lenB) {// 链表A比链表B长,A先移动
for i := 0; i < lenA - lenB; i++ {
headA = headA.Next
}
} else {// 链表B比链表A长,B先移动
for i := 0; i < lenB - lenA; i++ {
headB = headB.Next
}
} for headA != nil {
if headA == headB {
return headA
}
headA = headA.Next
headB = headB.Next
}
return nil;
} //获取链表长度
func getLen(head *ListNode) int {
var len int
for head != nil {
len++
head = head.Next
}
return len
}

后记

这道题也是面试题中经常遇到的“两个链表的第一个公共节点”。虽然难度是“简单”,但对我来说一点也不简单。看评论和题解涨了很多姿势。

而且,一开始用的是 PHP,可怎么都通不过,不得不用 Go 重写了一遍,倒是很快通过了。

附上 PHP 版本:

  • 题解1 PHP版本
class ListNode {
public $val = 0;
public $next = null;
function __construct($val) {
$this->val = $val;
}
} /**
* @param ListNode $headA
* @param ListNode $headB
* @return ListNode
*/
function getIntersectionNode($headA, $headB) {
$hashMap = [];
while ($headA) {
$hashMap[] = $headA;
$headA = $headA->next;
}
while ($headB) {
if (in_array($headB, $hashMap)) {
return $headB;
}
$headB = $headB->next;
}
return null;
}
  • 题解2 PHP版本
function getIntersectionNode($headA, $headB) {
if ($headA == null || $headB == null) {
return null;
} $pA = $headA;
$pB = $headB;
while ($pA != $pB) {
$pA = $pA == null ? $headB : $pA->next;
$pB = $pB == null ? $headA : $pB->next;
}
return $pA;
}
  • 题解3 PHP版本
function getIntersectionNode($headA, $headB) {
$lenA = getListLength($headA);
$lenB = getListLength($headB); $diffLen = $lenA > $lenB ? $lenA - $lenB : $lenB - $lenA;
$headLong = $lenA > $lenB ? $headA : $headB;
$headShort = $lenA > $lenB ? $headB : $headA; //先在长链表上走几步,再同时在两个链表上遍历
for ($i = 0; $i < $diffLen; $i++) {
$headLong = $headLong->next;
}
while ($headLong != $headShort) {
$headLong = $headLong->next;
$headShort = $headShort->next;
}
return $headLong;
} /**
* 获取链表的长度
*/
function getListLength($head)
{
$length = 0;
$current = $head;
while ($current != null) {
$length++;
$current = $current->next;
}
return $length;
}

LeetCode#160-Intersection of Two Linked Lists-相交链表的更多相关文章

  1. 【LeetCode】Intersection of Two Linked Lists(相交链表)

    这道题是LeetCode里的第160道题. 题目讲的: 编写一个程序,找到两个单链表相交的起始节点. 如下面的两个链表: 在节点 c1 开始相交. 示例 1: 输入:intersectVal = 8, ...

  2. 160 Intersection of Two Linked Lists 相交链表

    编写一个程序,找到两个单链表相交的起始节点.例如,下面的两个链表:A:           a1 → a2                            ↘                   ...

  3. Leetcode 160 Intersection of Two Linked Lists 单向链表

    找出链表的交点, 如图所示的c1, 如果没有相交返回null. A:             a1 → a2                               ↘               ...

  4. [LeetCode] 160. Intersection of Two Linked Lists 解题思路

    Write a program to find the node at which the intersection of two singly linked lists begins. For ex ...

  5. [LeetCode]160.Intersection of Two Linked Lists(2个链表的公共节点)

    Intersection of Two Linked Lists Write a program to find the node at which the intersection of two s ...

  6. [LeetCode] 160. Intersection of Two Linked Lists 求两个链表的交集

    Write a program to find the node at which the intersection of two singly linked lists begins. For ex ...

  7. LeetCode 160. Intersection of Two Linked Lists (两个链表的交点)

    Write a program to find the node at which the intersection of two singly linked lists begins. For ex ...

  8. Leetcode 160. Intersection of two linked lists

    Write a program to find the node at which the intersection of two singly linked lists begins. For ex ...

  9. Java for LeetCode 160 Intersection of Two Linked Lists

    Write a program to find the node at which the intersection of two singly linked lists begins. For ex ...

  10. ✡ leetcode 160. Intersection of Two Linked Lists 求两个链表的起始重复位置 --------- java

    Write a program to find the node at which the intersection of two singly linked lists begins. For ex ...

随机推荐

  1. [简单路径] Useful Decomposition

    Ramesses knows a lot about problems involving trees (undirected connected graphs without cycles)! He ...

  2. HDU 1847-Good Luck in CET-4 Everybody!-博弈SG函数模板

    Problem Description 大学英语四级考试就要来临了,你是不是在紧张的复习?也许紧张得连短学期的ACM都没工夫练习了,反正我知道的Kiki和Cici都是如此.当然,作为在考场浸润了十几载 ...

  3. AQS机制

    一,Lock接口 锁是用来控制多个线程访问共享资源的方式,一般来说,一个锁能够防止多个线程同时访问共享资源(但是有些锁可以允许多个线程并发的访问共享资源,比如读写锁).在Lock接口出现之前,Java ...

  4. 《JavaScript 模式》读书笔记(4)— 函数5

    这一篇是函数部分的最后一篇.我们来聊聊Curry化. 十.Curry 这部分我们主要讨论Curry化和部分函数应用的内容.但是在深入讨论之前,我们需要先了解一下函数应用的含义. 函数应用 在一些纯粹的 ...

  5. Selenium系列(十六) - Web UI 自动化基础实战(3)

    如果你还想从头学起Selenium,可以看看这个系列的文章哦! https://www.cnblogs.com/poloyy/category/1680176.html 其次,如果你不懂前端基础知识, ...

  6. 搬运工 Logstash

    1,Logstash 简介 Logstash是一个开源数据收集引擎,具有实时管道功能.Logstash可以动态地将来自不同数据源的数据统一起来,并将数据标准化到你所选择的目的地. 通俗的说,就是搬运工 ...

  7. 一文彻底读懂MySQL事务的四大隔离级别

    前言 之前分析一个死锁问题,发现自己对数据库隔离级别理解还不够清楚,所以趁着这几天假期,整理一下MySQL事务的四大隔离级别相关知识,希望对大家有帮助~ 事务 什么是事务? 事务,由一个有限的数据库操 ...

  8. Q - 迷宫问题 POJ - 3984(BFS / DFS + 记录路径)

    Q - 迷宫问题 POJ - 3984 定义一个二维数组: int maze[5][5] = { 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, ...

  9. Rust入坑指南:万物初始

    有没有同学记得我们一起挖了多少个坑?嗯-其实我自己也不记得了,今天我们再来挖一个特殊的坑,这个坑可以说是挖到根源了--元编程. 元编程是编程领域的一个重要概念,它允许程序将代码作为数据,在运行时对代码 ...

  10. js 实现浏览器全屏效果

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...