zoukankan      html  css  js  c++  java
  • 【编程题目】编程判断俩个链表是否相交 ☆

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

    看到这个题目我很困惑。如果链表的结构是下面这个样子

    typedef struct ListNode
    {
        int m_Value;
        ListNode * p_Next;
    }ListNode;

    那么一旦有相交,链表的后端就都是一模一样的了啊?因为交叉点只可能有一个p_Next,那只要判断两个链表最后一个不为空的结点是否相同就可以了。

    对有环列的,那环只能出现在链表最后面了。扩展的第1、2问只要对两个链表做两层循环判断就可以了。

    但总觉得我理解的不对。对于带环链表的创建我也有点困惑。

    在网上看了看,我的理解居然是对的。 我判断环的方法是用一个指针数组缓存已经出现过的点,比较新的点在之前是否出现过。 对于带环的相交判断是分开环的部分和无环的部分,先判断没有环的部分是否相交,如果不相交,再判断一个环结点是否在另一个环中出现。

    缺点:需要数组来缓存,链表最大长度有限制。

    /*
    第 7 题(链表)
    微软亚院之编程判断俩个链表是否相交
    给出俩个单向链表的头指针,比如 h1,h2,判断这俩个链表是否相交。
    为了简化问题,我们假设俩个链表均不带环。
    问题扩展:
    1.如果链表可能有环列?
    2.如果需要求出俩个链表相交的第一个节点列?
    start time = 11:10
    end time = 
    */
    
    #include <stdio.h>
    #include <stdlib.h>
    
    typedef struct ListNode
    {
        int m_Value;
        ListNode * p_Next;
    }ListNode;
    
    //无环
    bool isCross(ListNode * p1, ListNode * p2)
    {
        ListNode * x = p1;
        ListNode * y = p2;
        while(x->p_Next != NULL)
        {
            x = x->p_Next;
        }
        while(y->p_Next != NULL)
        {
            y = y->p_Next;
        }
        return (x == y);
    }
    
    //判断是否有环 若有返回环的交叉结点 没有返回NULL
    ListNode * isCircle(ListNode * pHead)
    {
        if(pHead == NULL)
            return NULL;
    
        ListNode * p[100];
        int n = 0;
        p[n] = pHead;
        n++;
        while(p[n - 1]->p_Next != NULL)
        {
            p[n] = p[n - 1]->p_Next;
            n++;
            for(int i = 0; i < n - 1; i++)
            {
                if(p[i] == p[n - 1])
                {
                    return p[i];
                }
            }
            if(n >= 100)
            {
                printf("input is too large");
                return NULL;
            }
        }
        return NULL;
    }
    
    ListNode * isCrossWithCircle(ListNode * p1, ListNode *p2)
    {
        ListNode * c1 = isCircle(p1);
        ListNode * c2 = isCircle(p2);
        if(c1 == NULL && c2 == NULL) //都没有环
        {
            c1 = p1;
            c2 = p2;
            while(c1 != NULL)
            {
                while(c2 != NULL)
                {
                    if(c1 == c2)
                        return c1;
                    c2 = c2->p_Next;
                }
                c1->p_Next;
            }
        }
        else if(c1 != NULL && c2 != NULL) //都有环
        {
            ListNode * h1 = p1;
            ListNode * h2 = p2;
            while(h1 != c1) //判断非环部分是否相交
            {
                while(h2 != c2)
                {
                    if(h1 == h2)
                    {
                        return h1;
                    }
                    h2 = h2->p_Next;
                }
                h1 = h1->p_Next;
            }
            h1 = c1;
            h2 = c2;
            do //判断非环部分是否相交 第一个公共点是 两个交叉点中的任意一个
            {
                if(h1 == h2)
                {
                    return h1;
                }
                h1 = h1->p_Next;
            }while(h1 != c1);    
        }
    
        return NULL;
    }
    int main()
    {
        ListNode n1 , n2, n3, n4, n5, n6, n7, n8, n9;
        n1.m_Value = 1; n1.p_Next = &n2;
        n2.m_Value = 2; n2.p_Next = &n3;
        n3.m_Value = 3; n3.p_Next = &n7;
        n4.m_Value = 4; n4.p_Next = &n5;
        n5.m_Value = 5; n5.p_Next = &n6;
        n6.m_Value = 6; n6.p_Next = &n8;
        n7.m_Value = 7; n7.p_Next = &n8;
        n8.m_Value = 8; n8.p_Next = &n9;
        n9.m_Value = 9; n9.p_Next = &n7;
    
        //bool flag = isCross(&n1, &n4);
        ListNode * a = isCrossWithCircle(&n1, &n4);
        return 0;
    }

    网上发现,判断是否有环和环结点有专门的方法:

    http://www.cppblog.com/humanchao/archive/2008/04/17/47357.aspx

    一、判断链表是否存在环,办法为:

    设置两个指针(fast, slow),初始值都指向头,slow每次前进一步,fast每次前进二步,如果链表存在环,则fast必定先进入环,而slow后进入环,两个指针必定相遇。(当然,fast先行头到尾部为NULL,则为无环链表)程序如下:

    bool IsExitsLoop(slist *head)
    {
        slist *slow = head, *fast = head;
    
        while ( fast && fast->next ) 
        {
            slow = slow->next;
            fast = fast->next->next;
            if ( slow == fast ) break;
        }
    
        return !(fast == NULL || fast->next == NULL);
    }

    二、找到环的入口点

    当fast若与slow相遇时,slow肯定没有走遍历完链表,而fast已经在环内循环了n圈(1<=n)。假设slow走了s步,则fast走了2s步(fast步数还等于s 加上在环上多转的n圈),设环长为r,则:

    2s = s + nr
    s= nr

    设整个链表长L,入口环与相遇点距离为x,起点到环入口点的距离为a。
    a + x = nr
    a + x = (n – 1)r +r = (n-1)r + L - a
    a = (n-1)r + (L – a – x)

    (L – a – x)为相遇点到环入口点的距离,由此可知,从链表头到环入口点等于(n-1)循环内环+相遇点到环入口点,于是我们从链表头、与相遇点分别设一个指针,每次各走一步,两个指针必定相遇,且相遇第一点为环入口点。程序描述如下:

    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;
    }

    判断两个单链表是否相交,如果相交,给出相交的第一个点(两个链表都不存在环)。

    比较好的方法有两个:

    一、将其中一个链表首尾相连,检测另外一个链表是否存在环,如果存在,则两个链表相交,而检测出来的依赖环入口即为相交的第一个点。

    二、如果两个链表相交,那个两个链表从相交点到链表结束都是相同的节点,我们可以先遍历一个链表,直到尾部,再遍历另外一个链表,如果也可以走到同样的结尾点,则两个链表相交。

    这时我们记下两个链表length,再遍历一次,长链表节点先出发前进(lengthMax-lengthMin)步,之后两个链表同时前进,每次一步,相遇的第一点即为两个链表相交的第一个点。

    看过答案后修改代码:

    /*
    第 7 题(链表)
    微软亚院之编程判断俩个链表是否相交
    给出俩个单向链表的头指针,比如 h1,h2,判断这俩个链表是否相交。
    为了简化问题,我们假设俩个链表均不带环。
    问题扩展:
    1.如果链表可能有环列?
    2.如果需要求出俩个链表相交的第一个节点列?
    start time = 11:10
    end time = 
    */
    
    #include <stdio.h>
    #include <stdlib.h>
    
    typedef struct ListNode
    {
        int m_Value;
        ListNode * p_Next;
    }ListNode;
    
    ListNode * isCircle(ListNode * pHead) //判断是否有环,返回环的起始点指针
    {
        ListNode * slow = pHead;
        ListNode * fast = pHead;
    
        while(fast != NULL && fast->p_Next != NULL)
        {
            slow = slow->p_Next;
            fast = fast->p_Next->p_Next;
            if(slow == fast)
                break;
        }
        if(fast == NULL || fast->p_Next == NULL)
            return NULL;
    
        slow = pHead;
        while(slow != fast)
        {
            slow = slow->p_Next;
            fast = fast->p_Next;
        }
        return slow;
    }
    
    ListNode * isCross(ListNode * p1, ListNode * p2)
    {
        ListNode * c1 = isCircle(p1);
        ListNode * c2 = isCircle(p2);
        if(c1 == c2) //都不是带环的 或者有环且环相同 只可能在环结点前面相交
        {
            ListNode * h1 = p1;
            ListNode * h2 = p2;
            int len1 = 0;
            int len2 = 0;
            while(h1->p_Next != c1) //统计从起点到相同点有多少步,以及c1 c2之前的那个结点
            {
                len1++;
                h1 = h1->p_Next;
            }
            while(h2->p_Next != c2)
            {
                len2++;
                h2 = h2->p_Next;
            }
            if(h1 == h2) //无环且相交 或 有环但相交点在非环部分
            {
                h1 = p1;
                h2 = p2;
                if(len1 > len2)  //长度长的链先走多出来的部分,剩下的同步走,直到相同
                {
                    for(int i = 0; i < len1 - len2; i++)
                    {
                        h1 = h1->p_Next;
                    }
                }
                else
                {
                    for(int i = 0; i < len2 - len1; i++)
                    {
                        h2 = h2->p_Next;
                    }
                }
    
                while(h1 != h2)
                {
                    h1 = h1->p_Next;
                    h2 = h2->p_Next;
                }
                return h1;
    
            }
            else if(c1 != NULL) //恰在环交点相交
            {
                return c1;
            }
        }
        else if(c1 != NULL && c2 != NULL) //如果两个环不同
        {
            ListNode * h1 = c1; //判断在环1内能否找到环2的点
            do
            {
                if(h1 == c2)
                    return h1;
                h1 = h1->p_Next;
            }while(h1 != c1);
        }
    
        return NULL; //其他情况均不会相交
    }
    
    int main()
    {
        ListNode n1 , n2, n3, n4, n5, n6, n7, n8, n9;
        n1.m_Value = 1; n1.p_Next = &n2;
        n2.m_Value = 2; n2.p_Next = &n3;
        n3.m_Value = 3; n3.p_Next = &n7;
        n4.m_Value = 4; n4.p_Next = &n5;
        n5.m_Value = 5; n5.p_Next = &n6;
        n6.m_Value = 6; n6.p_Next = &n8;
        n7.m_Value = 7; n7.p_Next = &n8;
        n8.m_Value = 8; n8.p_Next = &n9;
        n9.m_Value = 9; n9.p_Next = &n7;
    
        ListNode * a = isCross(&n1, &n4);
        return 0;
    }
  • 相关阅读:
    【深入学习MySQL】MySQL的索引结构为什么使用B+树?
    【Python爬虫】爬了七天七夜,终于爬出了博客园粉丝数排行榜!
    【BAT面试题系列】面试官:你了解乐观锁和悲观锁吗?
    深入学习MySQL事务:ACID特性的实现原理
    深入学习Redis(5):集群
    深入学习Redis(4):哨兵
    谈谈微信支付曝出的漏洞
    深入学习Redis(3):主从复制
    深入学习Redis(2):持久化
    Spring中获取request的几种方法,及其线程安全性分析
  • 原文地址:https://www.cnblogs.com/dplearning/p/3902595.html
Copyright © 2011-2022 走看看