带环链表求入口结点——带环链表处理

求两个单链表相交的交点— 链表不带环

不带环的两个链表,只有两种情况。一种是平行也就是不相交,要不然就会相交形成Y型或者头尾相连的一字型。而只要相交,两个链表的尾结点一定是相同的,所以思路很清晰,判断尾结点是否相等就知道是否相交。

求交点:用两个指针,让指向较长的链表的指针先走长与短链表的长度差步,然后两个指针同时走,等到两个指针指向了同一个结点,那就是交点。

//求两个单链表相交的交点--- 链表不带环
pNode GetCorssNode(pNode pHead1, pNode pHead2)
{
    if (NULL == pHead1 || NULL == pHead2) {
        return NULL;
    }
    int len1 = 0;
    int len2 = 0;
    pNode pCur1 = pHead1;
    pNode pCur2 = pHead2;
    while (pCur1) {
        len1++;
        pCur1 = pCur1->pNext;
    }
    while (pCur2) {
        len2++;
        pCur2 = pCur2->pNext;
    }
    int diff = len1 - len2;
    while (0 != diff) {
        if (diff > 0) {
            pHead1 = pHead1->pNext;
            diff--;
        }
        else {
            pHead2 = pHead2->pNext;
            diff++;
        }
    }
    //走到这里两个指针距相交点有相同距离
    while (pHead1 != pHead2) {
        pHead1 = pHead1->pNext;
        pHead2 = pHead2->pNext;
    }
    return pHead1;
}

判断链表是否带环,返回相遇点

如果一个指针每次走一步,一个指针每次走两步,那么快的指针一定会追上慢的指针。如果快指针走到NULL了,那么肯定不带环。

所以按这个思路,返回快指针追上满指针的结点就可以了,这个相遇点一定在环里。

// 判断链表是否带环,返回相遇点---注意推导公式
pNode IsListWithCircle(pNode pHead1)
{
    if (NULL == pHead1) {
        return NULL;
    }
    //一快一慢,若相遇则有环,若快的走到空就不带环
    pNode pFst = pHead1;
    pNode pLow = pHead1;
    while (pFst && pFst->pNext) {
        pFst = pFst->pNext->pNext;
        pLow = pLow->pNext;
        if (pFst == pLow) {
            return pFst;
        }
    }
    return NULL;
}

获取环的入口点

这里写图片描述

方法:一个指针指向相遇点,另一个指针指向链表头结点,两个指针同时走,当两个指针指向相同,则为入口点。

证明:

对遇点列方程,慢指针的走到遇点的路程 * 2 = 快指针走到遇点经过的路程

2 *( L + X) = L + X + n*R

其中n是快指针可能已经绕的圈数,因为如果L特别长,快指针就已经饶了n圈. 1 <= n

解得: L = n*R -X = (n -1) *R + R- X

上式如果模R,余 R - X,

也就是说如果一个指针从头走,一个指针从遇点走,不论指向遇点的指针走多少圈,最后一定会在入口点相遇。

证毕!

// 获取环的入口点
pNode GetCircleEnter(pNode pHead1, pNode pMeetNode)
{
    if (NULL == pHead1 || NULL == pMeetNode) {
        return NULL;
    }
    //两个同速指针,一个从起点一个从相遇点走
    pNode pStr = pHead1;
    while (pStr != pMeetNode) {
        pStr = pStr->pNext;
        pMeetNode = pMeetNode->pNext;
    }
    return pStr;
}

// 获取环的长度
int GetCircleLen(pNode pMeetNode)
{
    if (NULL == pMeetNode) {
        return -1;
    }
    pNode pCur = pMeetNode;
    int count = 1;
    pCur = pCur->pNext;
    while (pCur != pMeetNode) {
        count++;
        pCur = pCur->pNext;
    }
    return count;
}

判断两个链表是否相交,链表可能带环

第一种情况,两个都不带环。用上述的方法判断就好了。

第二种情况,有一个带环。那一定不相交,因为只要两个相交了,那么环肯定是公共部分。

第三种情况,两个都带环。有两种相交的情况,相交点在环内和环外。

这里写图片描述

但是处理方法是一样的,分别找到两个链表在环里的相遇点,让一个点不动,遍历一遍所在的环,如果找到另一个相遇点,那么两个遇点就是在一个环里————即两链表相交。

// 判断两个链表是否相交,链表可能带环
int IsSListCrossWithCircle(pNode pHead1, pNode pHead2)
{
    if (NULL == pHead1 || NULL == pHead2) {
        return -1;
    }
    pNode withCir1 = IsListWithCircle(pHead1);
    pNode withCir2 = IsListWithCircle(pHead2);
    //两个都没环
    if (!withCir1 && !withCir2) {
        return IsSListCross(pHead1, pHead2);
    }
    //两个都有环才有可能是相交的
    if (withCir1 && withCir2) {
        if (withCir1 == withCir2) {
            return 2;
        }
        pNode pCur = withCir1;
        pCur = pCur->pNext;
        while (pCur != withCir1) {
            if (pCur == withCir2) {
                return 2;
            }
            pCur = pCur->pNext;
        }
        return -1;
    }
    //仅有一个有环,肯定不相交
    return -1;
}

猜你喜欢

转载自blog.csdn.net/hanzheng6602/article/details/80007503