17_7_16:判断两个链表是否相交,若相交,求交点。如果链表带环呢?

1.【基础题】–1.判断两个链表是否相交,若相交,求交点。(假设链表不带环)2.判断两个链表是否相交,若相交,求交点。(假设链表可能带环)【升级版】

2.【附加题】–请问下面的程序一共输出多少个“-”?

#include <stdio.h>
#include <unistd.h>

int main(void) 
{ 
int i; 
for(i=0; i<2; i++){ 
fork(); 
printf("-"); 
} 

return 0; 
}

**

基础题:

**
定义的单链表节点

#include <iostream> //采用C++编译环境。

typedef struct ListNode
{
    int _val;
    ListNode* _pNext;
}Node, *PNode;

在代码中定义的一些函数

//获取环中节点中快慢指针的碰撞点。
PNode Get_Impact_Point(PNode pHead);
//获取带环链表中环的入口点
PNode Get_Entrance_Point(PNode pHead);
//判断两个链表是否相交,若相交,求交点。(假设链表不带环)
PNode Is_Has_Intersection(PNode pHead_1, PNode pHead_2);
//判断两个链表是否相交,若相交,求交点。(假设链表可能带环)【升级版】
PNode Is_Has_Intersection_2(PNode pHead_1, PNode pHead_2);

1.判断两个链表是否相交,若相交,求交点。(假设链表不带环)

思路:
不带环的两个链表相交,表示从交节点开始,两个链表中的节点一直相同。
那么两个链表的最后一个节点也相同。可以当做一个判断条件。
如果两个不带环链表相交,则将其中一个链表的头尾节点相连,构成一个带环的单链表。
这样问题就变为了,求带环单链表的环的入口点的问题。这里写图片描述

PNode Is_Has_Intersection(PNode pHead_1, PNode pHead_2)
{
    if ((NULL == pHead_1) || (NULL == pHead_2)) //当有空链表时,没有交点。
        return NULL;

    if (pHead_1 == pHead_2) //如果两个链表是通过一个链表的情况。
        return pHead_1;

    PNode pTail_1 = pHead_1;  //用来指向pHead_1的尾节点
    PNode pTail_2 = pHead_2;  //用来指向pHead_2的尾节点

    while (pTail_1->_pNext)   
        pTail_1 = pTail_1->_pNext;  
    while (pTail_2->_pNext)
        pTail_2 = pTail_2->_pNext;

    if (pTail_1 != pTail_2)  //两个链表最后一个节点不相等,说明没有相交。
        return NULL;  

    //走到这里,说明pTail_1 = pTail_2。两个链表相交,下面开始求交点。
    //将其中一个链表的头结点与尾节点相连。最终两个链表会构成一带环单链表
    //问题变为求带环单链表的环的入口了。

    //1,将两个单链表变为带环单链表(pHead_2为新的头指针)
    pTail_1->_pNext = pHead_1;
    //2,求环的入口。(调用写的函数,前一篇博文已写,后面也会给出)
    PNode pCross = Get_Entrance_Point(pHead_2);

    return pCross;
}

2.判断两个链表是否相交,若相交,求交点。(假设链表可能带环)【升级版】
思路:
先判断两个链表是否是同一个链表的情况。
1,相同,直接返回头结点。
2,不相同,在往下走。
再判断两个链表是否带环。
1,都不带环—>判断两个不带环单链表是否相交,若相交,求交点。
2,一个带环,一个不带环—->不想交
3,两个链表都带环—->1,交点在环外,2,交点再环内,3,不相交
通过两个链表环的入口点是否相等,来判断交点在环外还是环内。
若相等,则在环外或者就是入口点处。此时相交。–》去掉环,变为求两个不带环单链表求交点交点
若不相等,则判断两个入口点是否在通过一个环中,若在环中,则两个入口点都是交点。否则,不相交。
这里写图片描述

PNode Is_Has_Intersection_2(PNode pHead_1, PNode pHead_2)
{
    //存在空链表,不存在交点
    if ((NULL == pHead_1) || (NULL == pHead_2))
        return NULL;

    //如果两个链表是同一个链表,则直接返回头结点。
    if (pHead_1 == pHead_2)
        return pHead_1;

    PNode pCross = NULL; //交点

    //获取链表快慢指针的碰撞点,如果存在,则说明链表有环
    PNode pImpact_1 = Get_Impact_Point(pHead_1);
    PNode pImpact_2 = Get_Impact_Point(pHead_2);

    //都不带环--->判断两个不带环单链表是否相交,若相交,求交点。
    if ((NULL == pImpact_1) && (NULL == pImpact_2))
    {
        pCross = Is_Has_Intersection(pHead_1, pHead_2);
        return pCross;
    }

    //一个带环,一个不带环---->不相交
    if (!pImpact_1 || !pImpact_2) 
        return NULL;

    //两个链表都带环---->若相交,环是公有的,则判断链表1的碰撞点是否在链表2的环中
    //两个链表都带环的情况。
    //有三种情况。
    //1,不相交。
    //2,相交,交点在环外。
    //3,相交,交点在环内。


    //为了不重复定义变量,浪费空间,暂时用pImpact指向返回的环入口点。
    pImpact_1 = Get_Entrance_Point(pHead_1);
    pImpact_2 = Get_Entrance_Point(pHead_2);

    //此时,带环链表有可能相交于环内,有可能不相交
    if (pImpact_1 != pImpact_2)
    {
        PNode pCur = pImpact_2;   //作为遍历pHead_2的指针 
        //需要注意的是,在遍历pHead_2时,如果pHead_1的入口点不在pHead_2的环中,会死循环
        //所以,需要从pHead_2的环入口点,开始遍历,直到再次遇到入口点。
        while (pCur != pImpact_1)
        {
            pCur = pCur->_pNext;
            if (pCur == pImpact_2) //防止在环内一直遍历,而死循环
                break;
        }

        //pHead_1的入口点pImpact_1在pHead_2的环中,即相交,随便返回一个入口点
        if (pCur == pImpact_1)
        {
            return pImpact_1;
        }
        else  //否则,不相交
        {
            return NULL;
        }
    }

    //pImpact_1 == pImpact_2
    //此时,两个链表有一个公共环,并且,节点在环外。--》去掉环的影响,变为求两个不带环单链表求交点问题
    PNode pTemp = pImpact_1->_pNext; //记录,环的入口的下一节点。
    pImpact_1->_pNext = pHead_1; //去掉旧环,构成一个新环(pHead_2为头结点)
    //获取新环的入口点,即原来两个链表的交点
    pCross = Get_Entrance_Point(pHead_2);
    //去掉新环,还原旧环
    pImpact_1->_pNext = pTemp; 

    return pCross;
}

3.下面给出上述两个函数中调用的自定义函数

//获取环中节点中快慢指针的碰撞点。
PNode Get_Impact_Point(PNode pHead)
{
    if (NULL == pHead)
        return NULL;

    PNode pFast = pHead; //快指针每次走两步
    PNode pSlow = pHead; //慢指针每次走一步

    while ((NULL != pFast) && (NULL != pFast->_pNext))
    {
        pFast = pFast->_pNext->_pNext;
        pSlow = pSlow->_pNext;

        if (pFast == pSlow)
            break;
    }

    if ((NULL == pFast) || (NULL == pFast->_pNext)) //不可使用pFast!=pSlow作为判断条件,当链表只有一个元素时,会出错。
        return NULL;

    return pFast;
}
//获取带环链表中环的入口点
PNode Get_Entrance_Point(PNode pHead)
{
    PNode pImpact = Get_Impact_Point(pHead);
    if (NULL == pImpact) //链表中不存在环
        return NULL; 

    //头结点到入口点的长度设为:X
    //环的长度设为R
    //碰撞点到入口的长度设为:R1
    //通过计算(前一篇博文中写有),可以推到出存在某一个n(n为非负整数),使得:X = nR + R1.
    //所以,头结点和碰撞点同时出发, 最终会在入口点相遇。
    while (pImpact != pHead)
    {
        pImpact = pImpact->_pNext;
        pHead = pHead->_pNext;
    }
    //此时,两者都在入口点处,返回入口点位置。
    return pImpact; 
}

**

附加题

**
关于fork函数的知识,推荐博文:http://blog.csdn.net/jason314/article/details/5640969
里面对于fork进行了深入的讲解。而且例子也比较好。也包括这个例子。
下面,呈现在ubuntu下运行的结果图。
li这里写图片描述
结果打印了6个‘-’
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值