判断俩个链表是否相交

来源:互联网 发布:广州学校数据 编辑:程序博客网 时间:2024/05/16 17:38

《编程之美》上的一道微软亚院题,判断两个两个链表是否相交,难度系数低。

描述如下:

给出俩个单向链表的头指针,比如h1,h2,判断这俩个链表是否相交。 
为了简化问题,我们假设俩个链表均不带环。 
问题扩展: 
1.如果链表可能有环列? 
2.如果需要求出俩个链表相交的第一个节点列? 


逻辑分析:

1、先考虑不带环的链表,最直观的方法就是,遍历h2链表的每一个元素,是否在h1中出现,最坏时间复杂度O(length(h1)*length(h2))。显然,O(n^2)的复杂度,太差了。


2、借助空间,构造一个哈希表,将h1存入哈希表,然后针对h2逐个查询,是否在哈希表中存在,借助哈希表空间复杂度O(length(h1)),实现了O(length(h1)+length(h2))的时间复杂度。此时已为线性O(n)。


3、上面的两种想法,通俗来讲,都是所谓的“无脑思路”,即不通过数学或者逻辑上的分析,而采取的暴力或者伪暴力方法解决。实际上,通过简单的分析,我们知道,两个无环链表,如果有交集,那么交集之后的点必然是重合的。换个角度,如果两个无环链表相交,那么最后一个节点一定是公有的,而我们很容易能得到链表的最后一个节点,这成了我们简化解法的一个主要突破口。那么,我们只要判断俩个链表的尾指针是否相等。相等,则链表相交;否则,链表不相交。
所以,先遍历第一个链表,记住最后一个节点。然后遍历第二个链表,到最后一个节点时和第一个链表的最后一个节点做比较,如果相同,则相交,否则,不相交。这样我们就得到了一个时间复杂度,它为O((Length(h1) + Length(h2)),而且只用了一个额外的指针来存储最后一个节点。这个方法时间复杂度为线性O(N),空间复杂度为O(1),显然比哈希表存储法更胜一筹。


上述的第三种思路,已经揭开了问题的本质,那么现在扩展一下,如果链表有环,怎么办?

4、链表有环,乍一看,问题的复杂度要高得多,而我们原本的问题,就变成了这样的形式:

1)首先判断链表是否有环,如果无,那么比较h1和h2的尾结点是否重合,如果是,则相交,否则不相交。

2)如果链表有环,那么我们需要找出某种方法,来判断。

图文并茂的分析后,你会发现,有环其实也很简单,只不过,我们定位的不再是尾结点,而是“尾结点”,也就是链表成环的第一个结点,如果这个结点公有,那么链表相交,反过来说更容易理解,如果这个结点不是公有的,那么两个链表就是平行关系。(仔细想想为什么?)


5、现在,我们将面临着一个问题,如何判断链表有环?其实很简单,追及问题,弄两个指针,第一个每一次走一步,第二个每一次走两步,若干次循环后,如果有环,那么两个指针必然相遇。(什么,你问我没环,没环的话p2就提前领便当了,NULL了,还while个毛线。。。得出结论偷着乐吧。)


补充说明:如果一个有环,一个没环,怎么办,好吧,我又在秀下限了,有点智商的看到这里,都会表示绝对不会相交,没错,就是这么一目了然。


接下来,呈上一份代码:

 
//copyright@ KurtWang  July
//updated 2013.12.25  
struct Node  
{  
    int value;  
    Node * next;  
};  
//1.  
//bool  
//12  
bool isCircle(Node * head, Node *& circleNode, Node *& lastNode)  
{  
    Node * fast = head->next;  
    Node * slow = head;  
    while(fast != slow && fast && slow)  
    {  
        if(fast->next != NULL)  
            fast = fast->next;  
        
        if(fast->next == NULL)  
            lastNode = fast;  
        if(slow->next == NULL)  
            lastNode = slow;  
        
        fast = fast->next;  
        slow = slow->next;  
        
    }  
    if(fast == slow && fast && slow)  
    {  
        circleNode = fast;  
        return true;  
    }  
    else  
        return false;  
}  
//  
//2.  
//3.  
bool detect(Node * head1, Node * head2)  
{  
    Node * circleNode1;  
    Node * circleNode2;  
    Node * lastNode1;  
    Node * lastNode2;  
    
    bool isCircle1 = isCircle(head1,circleNode1, lastNode1);  
    bool isCircle2 = isCircle(head2,circleNode2, lastNode2);  
    
    //  
    if(isCircle1 != isCircle2)  
        return false;  
    //  
    else if(!isCircle1 && !isCircle2)  
    {  
        return lastNode1 == lastNode2;  
    }  
    //  
    else  
    {  
        Node * temp = circleNode1->next;  //updated and hyy  
        while(temp != circleNode1)    
        {  
            if(temp == circleNode2)  
                return true;  
            temp = temp->next;  
        }  
        return false;  
    }  
    
    return false;  
}  
 


文章到此就结束了,题毕竟是水题,不过之前有一位朋友和我说,他在腾讯面试,刚好就是被判断链表是否有环问倒了,另外,还有一个烂大街的问题,定位链表的倒数第k个结点,有兴趣自己试试。

另外,链表追及问题,july在程序员编程艺术系列的第九篇做了全面讲解,传送门:

http://blog.csdn.net/v_JULY_v/article/details/6447013
 



0 0
原创粉丝点击