算法学习之数据结构之链表是否相交,链表是否存在环

   当看到判断两链表是否相交,判断链表是否存在环时,就感觉不知道从何下手,原因是不知道什么是链表相交,什么是链表存在环,所以当明白概念的时候,发现这两个问题并不难,而且,其实两个单链表是否相交是和链表中存在环是有关系的。

  一、判断链表是否存在环。

  一个链表存在环,指的是,某个节点的next指向的是链表中在他之前的节点,这样在链表尾部形成环。(这个概念很重要。)
  弄明白概念后,对于下面两个问题就比较好解决了。
问题1,如何判断链表中存在环?
问题2如果链表存在环,如何找到环的入口点?
  问题1解决办法,弄懂环的概念以后,就能轻易解决了。设置两个指针(slow,fast),初始值都只想头结点,然后slow每次前进一步,fast每次前进两步,如果链表存在环,则fast先进入环,slow后进入环,最后fast会赶上slow两个指针会相遇,如果不存在环,则fast到尾节点则为null了,这样就不会相遇了。具体代码如下:
bool IsExitsLoop(slist *head)  
slist *slow, *fast;
slow = fas t= head;
while(fast != null && fast->next != null){
    slow=slow->next;
    fast=fast->next->next;
    if(fast == slow){
        break;
    }
} 
return (fast == null || fast->next == null) ? false : true;



  问题2解决办法,碰撞点p到连接点的距离=头指针到连接点的距离,则设 两个指针分别从头指针开始走和从相遇点开始走,这样他们会在环入口点相遇 。解释如下:
  当fast遇到slow时,slow还没走到尾节点,fast在环内走了n圈,设slow走了s个节点,则fast走了2s个节点,设环长为r则,fast走的距离等于slow走的距离加上在环内走的距离(nr ),则有 2s=s+nr,则s=nr。
  设链表长为L,头结点到环入口点的距离为a,环入口点到相遇点的距离为x,则有s=a+x, s=nr, a+x=nr, a+x=(n-1)r+r, r=L-a, a+x=(n-1)r+L-a, a=(n-1)r+L-a-x,a为起点到环入口点的距离,等于n-1个环的距离加上L-a-x为相遇点到换入口点的距离。于是设 两个指针分别从头指针开始走和从相遇点开始走,这样他们会在环入口点相遇 ,则再次相遇的点为环的入口点。具体分析过程看后面的手绘图。 具体代码如下:
slist* FindLoopPort(slist *head)
{
    slist *slow = head, *fast = head;

    while ( fast && fast->next ) 
    {
        slow = slow->next;
        fast = fast->next->next;
        if ( slow == fast ) break;
    }
    // 如果没有环则返回空
    if (fast == NULL || fast->next == NULL)
        return NULL;
    // 分别从头结点和相遇点开始走
    slow = head;
    // 两个节点一直走直到相遇
    while (slow != fast)
    {
         slow = slow->next;
         fast = fast->next;
    }
    // 返回相遇点
    return slow;
}


  二、判断两个单链表是否相交。

  方法一,其实这个和链表中是否有环是有关联的。两个单链表相交是Y型相交,而不是X型相交。所以两个链表相交,把其中一个链表的头结点接在另一个链表的尾节点后面,如果两链表相交,则连接后形成一个环。否则不形成环。具体分析过程看后面的手绘图。所以判断方法和判断链表中是否存在环的方法一样,前面加一步把两个链表连接就行了。
  方法二,因为是Y型相交,则从如果两个链表相交,那个两个链表从相交点到链表结束都是相同的节点,这样就可以采用以下办法。我们可以先遍历一个链表,直到尾部,再遍历另外一个链表,如果也可以走到同样的结尾点,则两个链表相交。
  接下来找出相交点。这时我们记下两个链表length,再遍历一次,长链表节点先出发前进(lengthMax-lengthMin)步,之后两个链表同时前进,每次一步,相遇的第一点即为两个链表相交的第一个点。具体代码如下:
Node *intersection(Node *head1, Node *head2)
if(!head1 || !head2)
   return NULL;
int len1 = 1, len2 = 1;
Node *p = head1, *q=head2, *fast, *slow;
bool result = false;
//判断两个链表是否相交,同时记下各个链表的长度
while(p->next){
    len1++;
    p=p->next;
}

while(q->next){
    len2++; 
    q=q->next;
}
// 尾节点相同则为相交,否则不想交
result=(p==q);
if(!result){
    return NULL; // 不存在则返回NULL
}else{
    // 链表长的先走lengthMax-lengthMin步
    int steps = abs(len1 – len2);
    if(len1 > len2){
        fast = head1;
        slow = head2;
    }else{
        fast = head2;
        slow = head1;
    }
    while(steps){
        fast = fast->next;
        steps –-;
    }
    
    // 之后两个链表同时前进,每次一步,相遇的第一点即为两个链表相交的第一个点。
    while(fast != slow){
        fast = fast->next;
        slow = slow->next;
    }
    return fast;
}


分析的图:

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值