【LeetCode题解】160_相交链表
160_相交链表
描述
编写一个程序,找到两个单链表相交的起始节点。
例如,下面的两个链表:
A: a1 → a2
↘
c1 → c2 → c3
↗
B: b1 → b2 → b3
在节点 c1 开始相交。
注意:
- 如果两个链表没有交点,返回
null. - 在返回结果后,两个链表仍须保持原有的结构。
- 可假定整个链表结构中没有循环。
- 程序尽量满足 O(n) 时间复杂度,且仅用 O(1) 内存。
致谢:
特别感谢 @stellari 添加此问题并创建所有测试用例。
解法一:哈希表
思路
首先遍历链表 A 的所有节点,并将每个节点的引用存入哈希表中。接着,遍历链表 B 的每个节点,如果某个节点的引用在哈希表中存在,返回该节点的引用;如果遍历完链表 B 的所有节点没有发现这样一个节点,则返回 null。
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) {
if (headA == null || headB == null) {
return null;
}
Set<ListNode> nodes = new HashSet<>();
ListNode temp = headA;
while (temp != null) {
nodes.add(temp);
temp = temp.next;
}
temp = headB;
while (temp != null) {
if (nodes.contains(temp)) {
return temp;
}
temp = temp.next;
}
return null;
}
}
复杂度分析:
- 时间复杂度:\(O(m+n)\),其中,\(m\) 表示链表 A 的节点数,\(n\) 表示链表 B 的节点数,最坏的情况下,需要遍历两个链表的所有节点
- 空间复杂度:\(O(m)\) 或者 \(O(n)\)
Python 实现
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def getIntersectionNode(self, headA, headB):
"""
:type head1, head1: ListNode
:rtype: ListNode
"""
if not headA or not headB:
return None
nodesA = set()
curr = headA
while curr:
nodesA.add(curr)
curr = curr.next
curr = headB
while curr:
if curr in nodesA:
return curr
curr = curr.next
return None
复杂度分析同上。
解法二:双指针(推荐)
思路
双指针解法顾名思义需要两个指针,假设指针 pA 和 pB 分别指向链表 A 和链表 B 的头结点,之后两个指针分别以步幅为 1 的速度向链表的尾部遍历,当指针 pA 遍历到链表 A 的尾节点时,将指针 pA 指向链表 B 的头部。同样地,当指针 pB 遍历到链表 B 的尾节点时,将指针 pB 指向链表 A 的头部。当两个指针相遇时,指针 pA 或者 pB 所指向的节点就是两个链表的相交节点。
为了说明双指针的求解思路,假设链表 A 和链表 B 的结构如下图所示,

其中,链表 A 包含 6 个节点,节点的值分别为 1、3、5、7、9 和 11;链表 B 包含 4 个节点,节点的值分别为 2、4、9 和 11,因此,两个链表的相交节点为 9。设链表 A 中不相交的部分(即蓝色部分的节点)长度为 \(L1\),链表 B 中不相交的部分(即黄色部分的节点)长度为 \(L2\),两个链表相交的部分(即红色部分的节点)长度为 \(L3\)。
如下图所示,当指针 pB 遍历到链表 B 的尾节点 11 时,指针 pA 遍历到链表 A 中节点 7 的位置,下一次遍历指针 pB 将处于链表 A 的节点 1 的位置。

同理,当指针 pA 遍历到链表 A 的尾节点 11 时,此时指针 pB 处于链表 A 中节点 3 的位置,下一次遍历指针 pA 将处于链表 B 的节点 2 位置。

再经过两次遍历后,指针 pA 将位于链表 B 中节点 4 的位置,而指针 pB 也将到达链表 A 的节点 4 的位置,下一次遍历两个指针将在节点 9(即相交节点)相遇。此时,两个指针走过的长度都为 \(L1 + L2 + L3\)。究其原因,可以将两个指针走过的“路程”看成 3 个部分,即蓝色部分、红色部分以及橙色部分,只是两个指针走过 3 个部分的顺序是不同的,指针 pA 先走蓝色部分而指针 pB 先走橙色部分,但是经过前 3 个部分后,两个指针走过的长度一定是相同的,因此在下一次遍历的时候两个指针一定会相遇。

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) {
if (headA == null || headB == null) {
return null;
}
ListNode pA = headA;
ListNode pB = headB;
while (pA != pB) {
pA = pA == null ? headB : pA.next;
pB = pB == null ? headA : pB.next;
}
return pA;
}
}
复杂度分析:
- 时间复杂度:\(O(L1 + L2 + L3) = O(n)\),如果两个链表存在相交节点,则经过 \(L1 + L2 + L3\) 的“长度”后,两个指针一定会相遇
- 空间复杂度:\(O(1)\),只需要保存两个引用
Python 实现
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def getIntersectionNode(self, headA, headB):
"""
:type head1, head1: ListNode
:rtype: ListNode
"""
if not headA or not headB:
return None
p_a, p_b = headA, headB
while p_a != p_b:
if p_a:
p_a = p_a.next
else:
p_a = headB
if p_b:
p_b = p_b.next
else:
p_b = headA
return p_a
复杂度分析同上。
【LeetCode题解】160_相交链表的更多相关文章
- [LeetCode题解]160. 相交链表 | 双指针 + 哈希表
方法一:双指针 解题思路 假设链表存在相交时,headA 的长度为 a + c,headB 的长度为 b + c.如果把 headA 连上 headB,headB 连上 headB 的话,当遍历这两个 ...
- LeetCode 160: 相交链表 Intersection of Two Linked Lists
爱写Bug(ID:iCodeBugs) 编写一个程序,找到两个单链表相交的起始节点. Write a program to find the node at which the intersectio ...
- Leetcode题目160.相交链表(简单)
题目描述 编写一个程序,找到两个单链表相交的起始节点. 如下面的两个链表: 在节点 c1 开始相交. 输入:intersectVal = 8, listA = [4,1,8,4,5], listB = ...
- 【LeetCode】160. 相交链表
题目 输入两个链表,找出它们的第一个公共节点. 如下面的两个链表: 在节点 c1 开始相交. 示例 1: 输入:intersectVal = 8, listA = [4,1,8,4,5], listB ...
- LEETCODE - 160【相交链表】
/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode ...
- LeetCode题解-147 对链表进行插入排序
对链表进行插入排序. 插入排序的动画演示如上.从第一个元素开始,该链表可以被认为已经部分排序(用黑色表示). 每次迭代时,从输入数据中移除一个元素(用红色表示),并原地将其插入到已排好序的链表中. 插 ...
- LeetCode题解-147 对链表进行插入排序 Medium
对链表进行插入排序. 插入排序的动画演示如上.从第一个元素开始,该链表可以被认为已经部分排序(用黑色表示). 每次迭代时,从输入数据中移除一个元素(用红色表示),并原地将其插入到已排好序的链表中. 插 ...
- LeetCode 题解 | 237. 删除链表中的节点
题目描述: 请编写一个函数,使其可以删除某个链表中给定的(非末尾)节点,你将只被给定要求被删除的节点. 现有一个链表 -- head = [4,5,1,9],它可以表示为: 示例 1: 输入: hea ...
- [LeetCode题解]109. 有序链表转换二叉搜索树 | 快慢指针 + 递归
题目描述 给定一个单链表,其中的元素按升序排序,将其转换为高度平衡的二叉搜索树. 本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1. 示例: 给定的有序链表: ...
随机推荐
- vim 安装vim-javascript插件--Vundle管理
最近看了一下node.js,但是写的时候,vim对js没有很好的提示.于是就安装插件来处理,准备安装vim-javascript.但是安装github上面的插件时,推荐用Vundle和pathogen ...
- tf中softmax_cross_entropy_with_logits与sparse_softmax_cross_entropy_with_logits
其实这两个都是计算交叉熵,只是输入数据不同. #sparse 稀疏的.稀少的 word_labels = tf.constant([2,0]) predict_logits = tf.constant ...
- Android Studio 集成 TFS,实现安卓移动开发的持续集成和交付(DevOps)
目录 1 集成TFS系统.... 1.1 概述.... 1.2 安装TFS插件.... 1.2.1 在线安装方式.... 1.2.2 离线安装方案.... 1.3 常见操作.... 1.3.1 新建G ...
- linux系统编程之进程(三):进程复制fork,孤儿进程,僵尸进程
本节目标: 复制进程映像 fork系统调用 孤儿进程.僵尸进程 写时复制 一,进程复制(或产生) 使用fork函数得到的子进程从父进程的继承了整个进程的地址空间,包括:进程上下文.进程堆栈. ...
- 5.WebAPI的Filter
1.WebApi的Filter介绍: 大家知道什么是AOP(aspect oriented programming)吗?它是可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添 ...
- 基于EasyUi的datagrid合并单元格JS写法
$('#dg').datagrid({ width: 'auto', height: 'auto', scrollbarSize: , queryParams: {}, url: 'kkkk', co ...
- linux 修改MTU值
Linux系统还可以通过如下方式查看.修改MTU值. 查看:cat /sys/class/net/eth0/mtu 设置:echo "1460" > /sys/class/n ...
- 纯文本-FileOutputStream的解码方式
1.通过string.getBytes(charsetNane)获得的字节数组,字节数组的编码方式,决定了FileOutStream写出文件的格式 例1:字节数组采用“GBK”编码,write生成的文 ...
- A - Subsequence (算法 二分 )
点击打开链接 A sequence of N positive integers (10 < N < 100 000), each of them less than or equal 1 ...
- jquery判断滚动到某个div显示底部按钮
判读滚动某个div显示底部按钮 <!DOCTYPE html> <html lang="zh-CN"> <head> <meta char ...