判断两链表是否相交,若相交求交点(链表可能带环)

链表不带环

思路:

1、碰到这个问题,第一印象是采用hash来判断,将两个链表的节点进行hash,然后判断出节点,这种想法当然是可以的。

2、当然采用暴力的方法也是可以的,遍历两个链表,在遍历的过程中进行比较,看节点是否相同。

3、第三种思路是比较奇特的。先遍历第一个链表到他的尾部,然后将尾部的next指针指向第二个链表(尾部指针的next本来指向的是null)。这样两个链表就合成了一个链表,判断原来的两个链表是否相交也就转变成了判断新的链表是否有环的问题了:即判断单链表是否有环?

这样进行转换后就可以从链表头部进行判断了,其实并不用。通过简单的了解我们就很容易知道,如果新链表是有环的,那么原来第二个链表的头部一定在环上。因此我们就可以从第二个链表的头部进行遍历的,从而减少了时间复杂度(减少的时间复杂度是第一个链表的长度)。
这种方法可以判断两个链表是否相交,但不太容易找出他们的交点。

4、仔细研究两个链表,如果他们相交的话,那么他们最后的一个节点一定是相同的,否则是不相交的。因此判断两个链表是否相交就很简单了,分别遍历到两个链表的尾部,然后判断他们是否相同,如果相同,则相交;否则不相交。

求交点
判断出两个链表相交后就是判断他们的交点了。假设第一个链表长度为len1,第二个问len2,然后找出长度较长的,让长度较长的链表指针向后移动|len1 - len2| (len1-len2的绝对值),然后在开始遍历两个链表,判断节点是否相同即可。

typedef struct NodeList
{
    int _data;
    struct NodeList* _next;
}Node;

//判断两链表是否相交,若相交求交点(链表不带环)
Node* CheckCross(Node* list1, Node* list2)
{
    if (NULL==list1 || NULL==list2)
    {
        return NULL:
    }

    Node* p1 = list1;
    Node* p2 = list2;

    int len1 = 0;
    int len2 = 0;
    int index = 0;

    while (p1->_next)
    {
        p1 = p1->_next;
        len1++;
    }
    while (p2->_next)
    {
        p2 = p2->_next;
        len2++;
    }
    if (p1 != p2)
    {
        return NULL;
    }

    index = abs(len1 - len2);

    if (len1 > len2)
    {
        p1 = list1;
        p2 = list2;
    }
    else
    {
        p1 = list2;
        p2 = list1;
    }
    //p1先走abs(len1-len2)步数次
    for (int i = 0; i < index; i++)
    {
        p1 = p1->_next;
    }
    while (p1 != p2)
    {
        p1 = p1->_next;
        p2 = p2->_next;
    }

    //返回相遇点
    return p1;

}

链表可能带环

思路:
1.首先获取到2个链表的长度,获得长度差L(因为环是共用的,所以这个差其实就是环外的差),获得2个链表的各个环入口点
2.先让一个长的链表从头开始走过L个节点。
3.再让短的链表从头与之同步后移,再保证2者不走到环入口的同时判断节点地址信息是否相同
4.若相同并未到环入口点,则相交于环外,返回相交点。
5.否则直接返回任意一个链表的环入口作为相交点即可。
图示:
这里写图片描述
代码:

typedef struct NodeList
{
    int _data;
    struct NodeList* _next;
}Node;

//求环的入口点函数
Node* CirclePoint(Node* list)
{
    Node* fast = list;
    Node* slow = list;
    Node* cur = list;

    while (fast && fast->_next)
    {
        fast = fast->_next->_next;
        slow = slow->_next;

        if (fast == slow)
        {
            break;
        }
    }

    if (fast == NULL || fast->_next == NULL)
    {
        return NULL;
    }

    while (cur != fast)
    {
        cur = cur->_next;
    }

    return cur; 
}

//判断带环的链表是否相交求交点(链表可能带环)
Node* IntersectionPoint(Node* list1, Node* list2)
{
    if (NULL == list1 || NULL == list2)
    {
        return NULL:
    }

    Node* p1 = list1;
    Node* p2 = list2;
    Node* Entry1 = CirclePoint(list1);
    Node* Entry2 = CirclePoint(list2);

    int len1 = 0;
    int len2 = 0;
    int index = 0;
    while (p1->_next)
    {
        p1 = p1->_next;
        len1++;
    }

    while (p2->_next)
    {
        p2 = p2->_next;
        len2++;
    }


    index = abs(len1 - len2);
    if (len1 > len2)
    {
        p1 = list1;
        p2 = list2;
    }
    else
    {
        p1 = list2;
        p2 = list1;
    }

    for (int i = 0; i < index; i++)
    {
        p1 = p1->_next;
    }

    while (p1 != Entry1 && p2 != Entry2 && p1 != p2)
    {
        p1 = p1->_next;
        p2 = p2->_next;
    }
    //结束循环有3种情况
    //1.2个链表任意一个或全部同时走到了环入口点,结束了循环
    //2.2个链表任意一个或全部同时走到了环入口点,并且pre=forth,结束了循环
    //3.没有到达任何一个环入口点,pre=forth结束了循环

    if (p1 = p2)
        return p1;
    else
        return Entry1;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值