判断俩个链表是否相交,若相交求交点(考虑带环和不带环)

问题描述(链表不带环):

一个比较经典的问题,判断两个链表是否相交,如果相交找出他们的交点。

判断俩个链表是否相交,若相交求交点(考虑带环和不带环)_第1张图片
思路:

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

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

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

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

下图是一个简单的演示:
判断俩个链表是否相交,若相交求交点(考虑带环和不带环)_第2张图片
这种方法可以判断两个链表是否相交,但不太容易找出他们的交点。

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

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

下面给出一个简单的实现:

typedef struct node_t

{

int data;//data

struct node_t *next; //next

}node;



node* find_node(node *head1, node *head2)

{

if(NULL == head1 || NULL == head2)

{

return NULL;//如果有为空的链表,肯定是不相交的

}

node *p1, *p2;

p1 = head1;

p2 = head2;

int len1 = 0;

int len2 =0;

int diff = 0;

while(NULL != p1->next)

{

p1 = p1->next;

len1++;

}

while(NULL != p2->next)

{

p2 = p2->next;

len2++;

}

if(p1 != p2) //如果最后一个节点不相同,返回NULL

{

return NULL;

}

diff = abs(len1 - len2);

if(len1 > len2)

{

p1 = head1;

p2 = head2;

}

else

{

p1 = head2;

p2 = head1;

}

for(int i=0; inext;

}

while(p1 != p2)

{

p1 = p1->next;

p2 = p2->next;

}

return p1;

}

问题描述(假设链表带环)
相当于求俩个链表的第一个公共结点,要么找到,要么为空,那么考虑到有链表可能有带环的情况,情况共分为下面三种:

(1)、两个链表都带环
分别获取两个链表环的入口点
判断入口点是否相同
如果入口点相同,临时修改链表为 Y 形状,处理完毕后恢复
如果入口点不相同,将一个环遍历一周看是否能遇到另外一个环的入口点(防止单独成环)

(2)、两个链表都不带环
都无环,当做 Y 形状处理
(3)、一个带环一个不带环
这种情况肯定不相交,画几个图就想明白了。
关于(1),可以看下面这个图:
判断俩个链表是否相交,若相交求交点(考虑带环和不带环)_第4张图片

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
            val(x), next(NULL) {
    }
};*/
class Solution {
public:
    // 找出链表的第一个公共结点
    ListNode* FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2) {

        // 第一个公共结点
        ListNode* commonNode = NULL;

        if(pHead1  ==  NULL || pHead2 == NULL )
            return NULL;

        // (1) 判断是否都有环
        ListNode* circleNode1; // 链表1 环上的结点(快慢指针相遇点),如果无环则为空
        ListNode* circleNode2; // 链表2 环上的结点(快慢指针相遇点),如果无环则为空

        circleNode1 = GetMeetNode(pHead1);
        circleNode2 = GetMeetNode(pHead2);

        // (2) 如果都有环  or 如果都无环  or 只有一个有环

        //  1. 都有环
        if (circleNode1 && circleNode2){
            // 链表1 环的入口点
            ListNode* circleEnterNode1 = GetCircleEnterNode(pHead1, circleNode1);
            // 链表2 环的入口点
            ListNode* circleEnterNode2 = GetCircleEnterNode(pHead2, circleNode2);
            // 如果入口点相同,临时修改链表为 Y 形状,处理完毕后恢复
            if(circleEnterNode1 == circleEnterNode2){
                // 保存
                ListNode* enterNodeNext = circleEnterNode1->next;
                // 设置为 Y 形,即无环
                circleEnterNode1->next = NULL;

                // 调用处理无环情况的函数
                commonNode =  GetFirstCommonNodeNoCircle(pHead1, pHead2);

                // 恢复
                circleEnterNode1->next = enterNodeNext;

            }// 如果入口点不同,将一个环遍历一周看是否能遇到另外一个环的入口点
            else{
                // 获取其中一个环的长度
                int circleLength = GetCircleLength(circleNode2);
                // 遍历一周看是否能遇到另外一个环的入口点,如果遇到则找到,否则未找到返回环
                ListNode* next = circleEnterNode2->next;
                // 遍历一圈
                while(circleLength--){
                    // 找到公共结点
                    if(next == circleEnterNode1){
                        commonNode = circleEnterNode1;     
                        break;
                    }
                    next = next->next;
                }
                // 未找到公共结点
                if(circleLength <= 0)
                    return NULL;

            }
        } // 2. 都无环
        else if (circleNode1 == NULL && circleNode2 == NULL){
            commonNode = GetFirstCommonNodeNoCircle(pHead1, pHead2);
        }


        // (3).其中一个无环, 肯定无公共结点
        return commonNode;
    }

private:


    // 获取带环链表环上的一个结点(快慢指针相遇点),如果不带环则返回空
    ListNode* GetMeetNode(ListNode* pHead){
        if(pHead == NULL)
            return NULL;
        ListNode* pFast = pHead;
        ListNode* pSlow = pHead;
        // 快慢指针法
        while(pFast && pFast->next){
            pFast = pFast->next->next;
            pSlow = pSlow->next;
            if(pFast == pSlow){
                return pFast;
            }
        }
        return NULL;
    }

    // 根据快慢指针相遇点,过去环的入口点
    ListNode* GetCircleEnterNode(ListNode* pHead, ListNode* meetNode){
        if(pHead == NULL || meetNode == NULL)
            return NULL;

        while(pHead != meetNode){
            pHead = pHead->next;
            meetNode = meetNode->next;
        }
        return pHead;
    }

    // 判断两个链表是否相交
    bool isCross(ListNode* pHead1, ListNode* pHead2){

        if(pHead1 == NULL  || pHead2 == NULL){
            return false;
        }

        while(pHead1->next)
            pHead1 = pHead1->next;
        while(pHead2->next)
            pHead2 = pHead2->next;
        if(pHead1 == pHead2)
            return true;
        return false;
    }

    // 获取链表长度
    int getListLength(ListNode* pHead){
        int length = 0;
        while(pHead){
            length++;
            pHead = pHead->next;
        }
        return length;
    }

    // 处理 Y 形状
    ListNode* GetFirstCommonNodeNoCircle(ListNode* pHead1, ListNode* pHead2){

        if(pHead1 == NULL  || pHead2 == NULL)
            return NULL;


        // 如果不相交,直接返回NULL
        if (!isCross(pHead1, pHead2))
            return NULL;


        int step = 0;
        int lengthList1 = getListLength(pHead1);
        int lengthList2 = getListLength(pHead2);
        step = lengthList1 - lengthList2;

        if(step > 0){
            while(step--)
                pHead1 = pHead1->next;
        }
        else if(step<0){
            while(step++)
                pHead2 = pHead2->next;
        }

        while(pHead1 != pHead2){
            pHead1 = pHead1->next;
            pHead2 = pHead2->next;
        }
        return pHead1;    
    }

    // 获取带环链表环的长度
    int GetCircleLength(ListNode* meetNode){
        int length = 0;
        if(meetNode == NULL)
            return length;
        ListNode* next = meetNode->next;
        while(next != meetNode){
            length++;
            next = next->next;
        }
        return length;
    }

};

你可能感兴趣的:(数据结构)