一、题目

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

如下面的两个链表:



在节点 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. 关于利用python进行验证码识别的一些想法

    转载:@小五义http://www.cnblogs.com/xiaowuyi 用python加“验证码”为关键词在baidu里搜一下,可以找到很多关于验证码识别的文章.我大体看了一下,主要方法有几类: ...

  2. 树莓派3B+之Raspbian系统的安装

    概述 因为之前一段时间在研究物联网的原因,所以对树莓派这个东西早就有所耳闻.在我的印象里,树莓派几乎无所不能,它可以用来学编程. 搞物联网. 做服务器,甚至还能用它来进行渗透测试.终于,没禁的住诱惑, ...

  3. Ribbon负载均衡实现

    1,在之前的博文中,我通过eureka,consul,zookeeper 实现了注册中心,在实现的服务发现过程中,都是通过RstTemplate 来实现RPC 远程调用 RestTemplate 封装 ...

  4. 不可被忽视的操作系统( FreeRTOS )【1】

    把大多数人每个星期的双休过过成了奢侈的节假日放假,把每天23点后定义为自己的自由时间,应该如何去思考这个问题 ? 双休的两天里,不!是放假的两天里,终于有较长的时间好好的学习一下一直断断续续的Free ...

  5. Django之extra过滤

    extra extra(select=None, where=None, params=None, tables=None, order_by=None, select_params=None) 有些 ...

  6. C/C++知识总结 二 C/C++基础知识

    C/C++基础知识 C/C++基本格式说明 C/C++基本常识说明 C/C++基本格式说明 C语言基本格式 #include<stdio.h> //预处理文件 int main() //自 ...

  7. P - Sudoku Killer HDU - 1426(dfs + map统计数据)

    P - Sudoku Killer HDU - 1426 自从2006年3月10日至11日的首届数独世界锦标赛以后,数独这项游戏越来越受到人们的喜爱和重视. 据说,在2008北京奥运会上,会将数独列为 ...

  8. Git版本控制 Git、github,gitlab相关操作

    目录 关于版本控制 版本管理工具 集中式管理 分布式管理 git版本管理 git介绍 软件安装 Git工作状态 原理流程步骤 git基本操作 对文件进行修改 分支 共享仓库 创建共享仓库: 共享仓库上 ...

  9. 高性能/并发的保证-Netty在Redisson的应用

    前言 ​ Redisson Github: https://github.com/redisson/redisson ​ Redisson 官网:https://redisson.pro/ Redis ...

  10. 力软敏捷框架 jfGrid 使用例子之一

    百度了下关于力软敏捷框架 jfGrid的教程,基本没有,出来的全是jqGrid.好吧看来只能自己上手了 今天来讲讲列设置属性里数据格式化事件(formatter)的使用 常规的使用方式如上图所示. 先 ...