【数据结构---13】找到两个单链表相交的起始节点(假定不带环)

本文介绍了一种高效算法,用于查找两个单链表的相交起始节点,通过对比链表长度并调整指针位置,确保在O(n)时间复杂度和O(1)空间复杂度下解决此问题。

题目描述:

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

如下面的两个链表:

在这里插入图片描述

在节点 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) 内存

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

思路分析:

<1>如果其中一条链表为空,肯定不相交
<2>创建两个指针,对两个链表遍历,比较指针地址是否一致,值相等不代表相交,地址一致才是
<3>首先确认哪一条链表长,长的先走差值步,然后两个指针同时走,比较地址

代码实现:

struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB)
{
    //如果其中一条链表为空,肯定就没有交点
    if(headA==NULL || headB==NULL)
    {
        return NULL;
    }
    
    //计算链表的长度,确认A和B链表哪个先走
    int sizeA=1;
    int sizeB=1;
    struct ListNode * pCurA=headA;
    while(pCurA!=NULL)
    {
        sizeA++;
        pCurA=pCurA->next;
    }
    struct ListNode * pCurB=headB;
    while(pCurB!=NULL)
    {
        sizeB++;
        pCurB=pCurB->next;
    }   
    
    //长的先走,走的步数为差值步
    struct ListNode *pA=headA;
    if(sizeA>sizeB)
    {
        for(sizeA;sizeA-sizeB!=0;sizeA--)
        {
            pA=pA->next;
        }
    }
    struct ListNode *pB=headB;
    if(sizeA<sizeB)
    {
        for(sizeB;sizeB-sizeA!=0;sizeB--)
        {
            pB=pB->next;
        }
    } 
    
    //长度相等的话,同时走
    while(pA!=NULL)
    {
        if(pA==pB)
        {
            return pA;
        }
        pA=pA->next;
        pB=pB->next;
    }
    return NULL;
}
<think>好的,我需要帮用户找到两个单链表相交起始节点的C#实现。首先,我得回忆一下这个问题的常见解决方法。用户提到了使用快慢指针的方法,或者让指针交替遍历两个链表。根据引用[1]和[2],正确的做法是让两个指针分别从headA和headB出发,当其中一个走到末尾时,转到另一个链表的头部继续遍历。这样,如果链表相交两个指针最终会在交点相遇,因为它们的路程差会被抵消。 接下来,我要确保理解这个算法的正确性。假设链表A的长度为m,链表B的长度为n,且相交部分长度为k。那么指针p1走过的路程是m + (n - k),指针p2是n + (m - k),这样它们会在相交点相遇,因为总路程都是m + n - k,所以这个逻辑是对的。 然后,我需要将这个逻辑转化为C#代码。根据用户提供的Java示例,可以对应写出C#的版本。需要注意的是,C#的语法和Java类似,但变量声明和类名可能有所同。例如,ListNode类的定义应该包含一个next属性。 在代码中,需要处理两个指针的移动。当p1走到null时,将其指向headB;同样,p2走到null时指向headA。循环继续直到p1等于p2,这时候返回其中一个即可。如果两链表相交,最终p1和p2都会是null,循环结束,返回null,这符合要求。 还要注意引用[2]提到的,交点是指针相等而非数值相等,所以在判断时直接比较对象是否相同即可。此外,引用的示例代码中,当指针到达末尾时切换链表头部,这正是关键所在。 最后,确保代码的时间复杂度是O(m + n),符合用户提到的分析。同时,需要处理可能的边界情况,例如其中一个链表为空,或者两链表长度相同但相交等情况。 总结步骤: 1. 初始化两个指针p1和p2分别指向headA和headB。 2. 进入循环,移动指针,直到它们相遇或都为null。 3. 当指针走到末尾时,切换到另一链表的头部继续遍历。 4. 返回相遇的节点,即为相交起始节点。</think>### 分步解答 寻找两个单链表相交起始节点的C#实现步骤如下: #### 算法思路 1. **双指针交替遍历**:使用两个指针`p1`和`p2`分别从`headA`和`headB`出发同步移动 2. **路径补偿机制**:当指针到达链表末尾时,跳转到另一链表的头部继续遍历,以此消除两链表的长度差[^1] 3. **相交判断**:若存在相交节点,两指针最终会在相交点相遇;若相交,两指针会同时到达`null` #### C#代码实现 ```csharp public class Solution { public ListNode GetIntersectionNode(ListNode headA, ListNode headB) { ListNode p1 = headA, p2 = headB; while (p1 != p2) { p1 = (p1 == null) ? headB : p1.next; p2 = (p2 == null) ? headA : p2.next; } return p1; // 包含p1==p2==null的情况 } } ``` #### 关键点解析 1. **路径补偿原理**:假设链表A长度为$a$,链表B长度为$b$,相交部分长度为$c$,则遍历总路径满足: $$a + (b - c) = b + (a - c)$$ 保证两指针同时到达相交节点[^1] 2. **时间复杂度**:$O(m + n)$,空间复杂度$O(1)$ 3. **边界条件处理**: - 任一链表为空时直接返回`null` - 两链表长度相同时自动退化为同步遍历 #### 测试用例示例 ```csharp // 构造相交链表 ListNode common = new ListNode(8){ next = new ListNode(4) }; ListNode headA = new ListNode(4){ next = new ListNode(1){ next = common } }; ListNode headB = new ListNode(5){ next = new ListNode(0){ next = new ListNode(1){ next = common } } }; var result = new Solution().GetIntersectionNode(headA, headB); // 返回值为common节点 ``` 相关问题
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值