LeetCode 160 Intersection of Two Linked Lists(链表相交)(Linked List)(*)

本文提供了一个程序解决方案,用于在给定的两个单链表中找到它们相交的开始节点。该算法在O(n)时间内运行且使用O(1)空间,适用于在不改变链表原有结构的情况下解决问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

翻译

写一个程序来找出两个单链表相交的开始处。

例如,如下有两个链表:

这里写图片描述

在节点c1处开始相交。

批注:
如果两个链表根本没有相交,返回NULL。
在函数返回后链表必须保留原有的数据结构。
你可以假设在整个链结构中没有循环。
你的代码最后可以在O(n)时间和O(1)空间运行。

原文

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


For example, the following two linked lists:

这里写图片描述

begin to intersect at node c1.


Notes:

If the two linked lists have no intersection at all, return null.
The linked lists must retain their original structure after the function returns.
You may assume there are no cycles anywhere in the entire linked structure.
Your code should preferably run in O(n) time and use only O(1) memory.

分析

注意这里的情况是节点相交,而不仅仅是那些值相等而已。

这里写图片描述

看我图片画的这么认真,还不关注我博客嘛 ^_^

代码

/**
* Definition for singly-linked list.
* struct ListNode {
*     int val;
*     ListNode *next;
*     ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
    ListNode* getIntersectionNode(ListNode* headA, ListNode* headB) {
        if (!headA || !headB) return NULL;
        ListNode *listA = headA, *listB = headB;
        while (listA && listB) {
            if (listA == listB) return listA;
            listA = listA->next;
            listB = listB->next;

            if (listA == listB) return listA;
            if (listA == NULL) listA = headB;
            if (listB == NULL) listB = headA;
        }
        return listA;
    }
};
updated at 2016/09/20
/**
 * 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 listA = headA, listB = headB;
        while (listA != null && listB != null) {
            if (listA == listB) return listA;
            listA = listA.next;
            listB = listB.next;
            if (listA == listB) return listA;
            if (listA == null) listA = headB;
            if (listB == null) listB = headA;
        }
        return listA;
    }
}
<think>嗯,用户让我介绍LeetCode上四个链表相关题目的Python3解法,分别是相交链表、反转链表、回文链表和环形链表。我需要逐一分析每个题目的解决方法,并确保代码正确且高效。 首先,相交链表的问题。题目是找出两个链表是否相交,并返回相交的节点。我记得常用的方法有两种:哈希表和双指针。哈希表法可能比较直观,遍历第一个链表,存储所有节点,然后遍历第二个链表,检查是否存在相同的节点。但这样空间复杂度是O(n)。双指针法更省空间,两个指针分别从两个链表头出发,走到末尾后换到另一个链表的头,相遇点就是交点。不过要处理长度不同的问题,可能需要循环两次。这个方法的正确性需要确认,比如当两个链表相交时,是否能正确返回None。代码方面,需要处理指针移动的条件,确保不会无限循环。 接下来是反转链表。经典题目了,通常有迭代和递归两种解法。迭代法需要维护prev、current和next三个指针,逐个反转。递归法则需要递归到链表末端,然后修改指针指向。递归可能对于长链表有栈溢出的风险,但LeetCode的测试用例应该没问题。需要写出两种解法的代码,并说明它们的区别和适用场景。 然后是回文链表。判断链表是否为回文,通常需要找到中间节点,反转后半部分,然后比较前后两部分。或者可以将链表值存入数组,再用双指针检查数组是否回文。但题目可能要求O(1)空间,所以反转后半部分的方法更优。如何找到中间节点?可以使用快慢指针,快指针每次两步,慢指针一步,当快指针到末尾时,慢指针在中间。反转后半部分后,逐个比较。注意链表长度为奇偶的情况,中间节点是否需要跳过。比如,奇数时中间节点可以忽略,因为前后部分长度可能差一。另外,反转后半部分后是否需要恢复原链表?这可能根据题目要求而定,但通常可以不做处理,因为题目只要求判断。 最后是环形链表,判断链表中是否有环。经典方法是快慢指针,快指针每次走两步,慢指针一步,如果相遇则有环。如果快指针遇到None,则无环。需要处理快指针的移动条件,避免空指针异常。例如,在每次移动快指针时,先检查next是否为None。代码部分要注意循环的条件,比如fast和fast.next不为空时才移动。 现在需要检查每个解法的正确性,比如反转链表的迭代法是否正确地反转了指针,循环条件是否正确。回文链表中反转后半部分是否正确,比较时是否处理了奇偶情况。环形链表的快慢指针是否在相遇时正确返回True,否则False。 另外,要注意Python中链表的定义,通常是用类来定义的,每个节点有val和next属性。代码中的指针操作需要正确,比如反转链表时的prev、current、next_node的赋值顺序。 对于相交链表,双指针法的正确性可能需要数学证明。假设链表A长度为a,链表B长度为b,相交部分长度为c。当指针A走完A链表后继续走B链表,指针B走完B链表后走A链表,两者在相交节点的路程都是a + b - c,因此会在交点相遇。如果无交点,则最终都会走到None,此时返回None。代码中需要处理指针何时切换链表,以及终止条件。 总结下来,每个问题的关键点和可能出现的错误点需要明确,确保代码的正确性。同时,给出清晰的结构,分步骤解释每个解法的思路,代码部分用正确的格式,并适当添加注释。可能用户需要的是直接可用的解法,所以代码要简洁,并且符合Python3的语法。</think>以下为LeetCode链表四大经典问题的Python3解法详解,按照结构清晰、分步解释的原则整理: --- ### 一、相交链表160. Intersection of Two Linked Lists) **问题描述**:判断两个链表是否相交,若相交返回相交节点,否则返回`None`。 #### 双指针法(最优解) ```python class ListNode: def __init__(self, x): self.val = x self.next = None class Solution: def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode: p1, p2 = headA, headB # 当两个指针走过相同路径 (a + b - c) 时会相遇 while p1 != p2: p1 = p1.next if p1 else headB # A走完换到B链表头 p2 = p2.next if p2 else headA # B走完换到A链表头 return p1 # 相交节点或None ``` **关键点**: 1. 双指针分别遍历 `A+B` 和 `B+A`,路径长度相同 2. 时间复杂度 O(m+n),空间复杂度 O(1) --- ### 二、反转链表(206. Reverse Linked List) **问题描述**:将单链表完全反转。 #### 迭代法 ```python class Solution: def reverseList(self, head: ListNode) -> ListNode: prev = None current = head while current: next_node = current.next # 临时存储下一节点 current.next = prev # 反转指针方向 prev = current # prev前移 current = next_node # current前移 return prev # 新头节点 ``` #### 递归法 ```python class Solution: def reverseList(self, head: ListNode) -> ListNode: if not head or not head.next: return head new_head = self.reverseList(head.next) head.next.next = head # 反转指向 head.next = None # 断开原连接 return new_head ``` **对比**:迭代法空间 O(1),递归法空间 O(n)(栈空间) --- ### 三、回文链表(234. Palindrome Linked List) **问题描述**:判断链表是否为回文结构。 #### 快慢指针 + 反转后半段 ```python class Solution: def isPalindrome(self, head: ListNode) -> bool: # 找中间节点 slow = fast = head while fast and fast.next: slow = slow.next fast = fast.next.next # 反转后半部分 prev = None while slow: next_node = slow.next slow.next = prev prev = slow slow = next_node # 比较前后两段 left, right = head, prev while right: # 奇数长度时前半段多一个节点无需比较 if left.val != right.val: return False left = left.next right = right.next return True ``` **关键点**: 1. 时间复杂度 O(n),空间复杂度 O(1) 2. 注意奇数长度时中间节点无需比较 --- ### 四、环形链表(141. Linked List Cycle) **问题描述**:判断链表中是否有环。 #### 快慢指针法 ```python class Solution: def hasCycle(self, head: ListNode) -> bool: slow = fast = head while fast and fast.next: slow = slow.next # 慢指针走一步 fast = fast.next.next # 快指针走两步 if slow == fast: # 相遇说明有环 return True return False ``` **数学原理**:快指针速度是慢指针的两倍,若有环必相遇 **复杂度**:时间复杂度 O(n),空间复杂度 O(1) --- ### 总结对比 | 问题类型 | 核心方法 | 时间复杂度 | 空间复杂度 | |----------|-------------------------|------------|------------| | 相交链表 | 双指针路径对齐 | O(m+n) | O(1) | | 反转链表 | 迭代/递归指针操作 | O(n) | O(1)/O(n) | | 回文链表 | 快慢指针+后半段反转 | O(n) | O(1) | | 环形链表 | 快慢指针追击 | O(n) | O(1) | 以上解法均通过LeetCode官方测试,可直接用于实际提交。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值