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

复杂度分析同上。

解法二:双指针(推荐)

思路

双指针解法顾名思义需要两个指针,假设指针 pApB 分别指向链表 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_相交链表的更多相关文章

  1. [LeetCode题解]160. 相交链表 | 双指针 + 哈希表

    方法一:双指针 解题思路 假设链表存在相交时,headA 的长度为 a + c,headB 的长度为 b + c.如果把 headA 连上 headB,headB 连上 headB 的话,当遍历这两个 ...

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

    爱写Bug(ID:iCodeBugs) 编写一个程序,找到两个单链表相交的起始节点. Write a program to find the node at which the intersectio ...

  3. Leetcode题目160.相交链表(简单)

    题目描述 编写一个程序,找到两个单链表相交的起始节点. 如下面的两个链表: 在节点 c1 开始相交. 输入:intersectVal = 8, listA = [4,1,8,4,5], listB = ...

  4. 【LeetCode】160. 相交链表

    题目 输入两个链表,找出它们的第一个公共节点. 如下面的两个链表: 在节点 c1 开始相交. 示例 1: 输入:intersectVal = 8, listA = [4,1,8,4,5], listB ...

  5. LEETCODE - 160【相交链表】

    /** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode ...

  6. LeetCode题解-147 对链表进行插入排序

    对链表进行插入排序. 插入排序的动画演示如上.从第一个元素开始,该链表可以被认为已经部分排序(用黑色表示). 每次迭代时,从输入数据中移除一个元素(用红色表示),并原地将其插入到已排好序的链表中. 插 ...

  7. LeetCode题解-147 对链表进行插入排序 Medium

    对链表进行插入排序. 插入排序的动画演示如上.从第一个元素开始,该链表可以被认为已经部分排序(用黑色表示). 每次迭代时,从输入数据中移除一个元素(用红色表示),并原地将其插入到已排好序的链表中. 插 ...

  8. LeetCode 题解 | 237. 删除链表中的节点

    题目描述: 请编写一个函数,使其可以删除某个链表中给定的(非末尾)节点,你将只被给定要求被删除的节点. 现有一个链表 -- head = [4,5,1,9],它可以表示为: 示例 1: 输入: hea ...

  9. [LeetCode题解]109. 有序链表转换二叉搜索树 | 快慢指针 + 递归

    题目描述 给定一个单链表,其中的元素按升序排序,将其转换为高度平衡的二叉搜索树. 本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1. 示例: 给定的有序链表: ...

随机推荐

  1. 使用 Project Siena 生成一个 Windows Store 应用

    继 App Studio 之后微软又一力作 Project Siena [Win8 应用神器]给初学开发 或 对 Windows Store 应用感兴趣的同学们的一个福利,可以通过 一个简单的应用可以 ...

  2. EasyUI 让dialog中的treegrid的列头固定

    先上效果: 最主要是在treegrid要加上"fit:true "如果不加那么就会用diglog的滚动条,导致treegrid的头就没办法固定. Code<div id=&q ...

  3. Linux 防火墙iptables命令详解

    [转:原文链接] iptables -Fiptables -Xiptables -F -t mangleiptables -t mangle -Xiptables -F -t natiptables ...

  4. win10 打开sql server配置管理器

    win10 安装 sql server之后无法在开始菜单找到“sql server 配置管理器(SQL server configuration manager 1)在开始菜单中,无法找到 配置管理器 ...

  5. hdu 4325 Flowers(区间离散化)

    http://acm.hdu.edu.cn/showproblem.php?pid=4325 Flowers Time Limit: 4000/2000 MS (Java/Others)    Mem ...

  6. 20164317《网络对抗技术》Exp3 免杀原理与实践

    一.实验要求 1.1 正确使用msf编码器(0.5分),msfvenom生成如jar之类的其他文件(0.5分),veil-evasion(0.5分),加壳工具(0.5分),使用shellcode编程( ...

  7. Java基础学习篇---------this、object的学习

    一.this的学习方法 1.使用this调用构造方法市一定放在构造方法的首行 2.使用this调用构造方法时一定流出调用的出口 public class MyClass { public MyClas ...

  8. Weekly Contest 128

    1012. Complement of Base 10 Integer Every non-negative integer N has a binary representation.  For e ...

  9. C语言参数传递(值传递、地址传递)+二级指针

    参数传递 C语言参数传递一般分为:值传递和地址传递(本质上只有值传递) (注意:C语言中没有引用传递,C++才有引用传递,因为很多C语言环境是用C++编译器编译,使得C看起来支持引用传递,导致很多网上 ...

  10. firebug中html显示为灰色的原因总结

    1.被设置了display:none. 2.长.宽都为0.