37-判断链表是否相交【找出第一个公共结点】

本文介绍两种高效算法解决链表交集问题:一是通过重定向并比较节点;二是计算链表长度差,调整起点后再逐个比较。两种方法均能在O(m+n)时间内完成,并仅用O(1)额外空间。

一、问题描述

编写一个程序来查找两个单独链接列表的交集开始的节点。
例如,以下两个链表:
A:          a1 → a2
                   ↘
                     c1 → c2 → c3
                   ↗
B:     b1 → b2 → b3


开始在节点c1相交。
【提示】
如果两个链表完全没有交集,则返回null。
链接列表在函数返回后必须保留其原始结构。
您可能会认为整个链接结构中没有任何地方的循环。
您的代码应该最好在O(n)时间内运行,并只使用O(1)内存。

二、解题思路

思路一:一次重定向,再逐一比较 -- O(m+n)

1)将A重定向到B,得到一个新链表
2)将B重定向到A,得到一个等长的新链表
3)遍历这两个新链表,如果同时为空,说明并不会相交,
如果找到相同的元素,则该元素就是相交的元素

思路二:先补链表长度差,再逐一比较  --  O(m+n)

1)首先遍历两个链表得到他们的长度,就能知道哪个链表比较长,以及长链表比短链表多几个结点

2)第二次遍历时,在较长的链表上先走若干步,接着再同时在两个链表上遍历,找到第一个相同的结点就是他们第一个公共结点。

三、解题算法

思路一:一次重定向,再逐一比较

/*************************************************
Author:tmw
date:2018-4-14
*************************************************/
#include <stdio.h>
#include <stdlib.h>

typedef struct listnode
{
    int data;
    struct listnode* next;
}listnode;

listnode* getIntersectionNode( listnode* headA, listnode* headB )
{
    /**入口检查:当两者任意一个为空,则返回NULL**/
    if( headA == NULL || headB == NULL )
        return NULL;

    else
    {
        listnode* pa;
        listnode* pb;
        pa = headA;
        pb = headB;
        int flagA = 0;
        int flagB = 0;

        while( pa && pb ) /**约定链表没有头指针,首位即存有值**/
        {
            if( pa->data == pb->data )
                return pa;

            pa = pa->next;
            pb = pb->next;

            /**将A重定向到B,得到一个新链表**/
            /**flagA用于只重定向一次,避免程序重复重定向而死循环了**/
            if( pa == NULL && flagA == 0 )
            {
                pa = headB;
                flagA = 1;
            }

            /**将B重定向到A,得到一个等长的新链表**/
            /**flagB作用同flagA**/
            if( pb == NULL && flagB == 0 )
            {
                pb = headA;
                flagB = 1;
            }
        }
        /**还没找到,则说明两个链表没有交点**/
        return NULL;
    }
    return NULL;
}

思路二:先补链表长度差,再逐一比较  --  O(m+n)

/**
* 解法二:先补链表长度差,再逐一比较
**/
int listNodeLength( listNode* head )
{
    int len = 0;
    listNode* p = head;
    while(p)
    {
        len++;
        p = p->next;
    }
    return len;
}
listNode* findFistCommomNodeMethod1( listNode* head1, listNode* head2 )
{
    if( head1==NULL || head2==NULL ) return NULL;
    int len1 = listNodeLength(head1);
    int len2 = listNodeLength(head2);
    int gap = len1 - len2;
    //保证pLong始终指向的是长度长一点的链表,pShort指向的是长度短一点的链表
    listNode* pLong = head1;
    listNode* pShort = head2;
    if( len1 < len2 )
    {
        pLong = head2;
        pShort = head1;
        gap = len2 - len1;
    }

    //长链表先走
    int i;
    for( i=0; i<gap; i++ )
        pLong = pLong->next;

    //此时长链表和短链表的同时开始挪动
    while( pLong != NULL && pShort != NULL && pLong->data != pShort->data )
    {
        pLong = pLong->next;
        pShort = pShort->next;
    }
    return pLong;
}

 

梦想还是要有的,万一实现了呢~~~ヾ(◍°∇°◍)ノ゙~~~~

### 问题分析 在两个带头结点的单链表中查找公共后缀的起始结点,其实质是判断两个链表是否在某个结点之后完全重合。由于链表采用单向结构,无法直接从尾部向前遍历,因此需要通过特定策略实现高效的查找。 --- ### 算法设计思想 基本思路是利用双指针同步遍历两个链表: 1. **计算链表长度**:分别遍历 `str1` 和 `str2` 链表,计算它们的长度。 2. **对齐链表**:将较长链表的指针向前移动,使得两个指针距离链表末尾的距离相等。 3. **同步遍历**:同时移动两个指针,直到找到第一个相同的结点,即为公共后缀的起始位置。 此方法的时间复杂度为 O(n),空间复杂度为 O(1),具有较高的效率。 --- ### C++实现代码 ```cpp struct ListNode { char data; ListNode* next; }; ListNode* findCommonSuffix(ListNode* str1, ListNode* str2) { if (!str1 || !str2) return nullptr; // 计算两个链表的长度 int len1 = 0, len2 = 0; ListNode* p1 = str1; ListNode* p2 = str2; while (p1->next) { len1++; p1 = p1->next; } while (p2->next) { len2++; p2 = p2->next; } // 如果尾节点不相同,则没有公共后缀 if (p1 != p2) return nullptr; // 对齐链表 p1 = str1; p2 = str2; if (len1 > len2) { for (int i = 0; i < len1 - len2; i++) { p1 = p1->next; } } else { for (int i = 0; i < len2 - len1; i++) { p2 = p2->next; } } // 同步遍历直到找到公共结点 while (p1 != p2) { p1 = p1->next; p2 = p2->next; } return p1; } ``` --- ### 时间复杂度分析 - **遍历链表计算长度**:各进行一次,时间复杂度为 O(n)。 - **对齐操作**:最多移动较长链表长度差值次,时间复杂度为 O(n)。 - **同步遍历**:最坏情况下需要遍历整个链表一次,时间复杂度为 O(n)。 因此,整体时间复杂度为 **O(n)**,空间复杂度为 **O(1)**[^3]。 --- ### 相关问题 1. 如何判断两个链表是否相交? 2. 在不使用额外空间的前提下,如何检测链表是否有环? 3. 如果链表中每个结点存储的是整数,如何找出两个链表中和为特定值的结点对? 4. 如何反转链表并保持时间复杂度为 O(n),空间复杂度为 O(1)? 5. 给定一个链表和一个整数 k,如何找到从链表末尾开始数第 k 个结点?
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值