• 两链表求公共节点


    引言

    其实这道题目早在剑指Offer上就已经出现过,参见以前的这篇文章,但后来July前辈也有一片文章讲过类似问题,对比之下,之前的解法还不够全面,没有考虑到所有情况,这篇文章把这道题目作一个梳理。

    题目总结起来是这样:

    输入两个链表,判断它们是否有公共节点,返回bool。

    引申:找出它们的第一个公共节点的指针,如果没有公共节点则返回NULL。

    链表的定义如下:

    struct ListNode{
        int val;
        ListNode* next;
        ListNode(int x) : val(x), next(NULL) {};
    };

    输出函数:

    bool JudgeCommonListNode(ListNode* head1, ListNode* head2){
    }
    
    
    ListNode* FindFirstCommonListNode(ListNode* head1, ListNode* head2){
    } //引申,求第一个公共节点

    首先需要考虑空指针输入,有一个输入为NULL,就不会有交点。

    寻找两链表的第一个交点,简便的做法是求出两链表长度差diff后,让长的链表先走diff步,然后依次比较两链表节点,若相同则返回。

    但这一个思路的前提是:两个链表都不含环。

    因此,在考虑完空指针后,需要检验两个链表是否含有环。

    (1) 若一个有环一个无环,则肯定没有交点。

    (2) 若都无环,可以用上面的思路求出第一个交点;如果只要求返回bool,则比较一下两链表的尾节点是否相等即可。

    (3) 两链表都含有环的情况下,如果只需要返回bool值,我们就检验其中一个链表的环入口是否在另一个链表的环上,在环上就返回true,表明相交,不在环上返回false。

    如果要返回第一个交点呢?这时候我们可以分情况考虑:如果两个链表都是“勺型”,所谓勺型,就是指不是纯的环链表。那这两个勺型链表如果有交点,环部分肯定是已经百分百重叠了,第一个交点只能出现在“勺柄”上,既然是勺柄,自然就无环啦,因此,上面那个求两个无环链表的第一个交点的方法有可以拿来用了~

    如果两个链表都有环,而且至少有一个是纯环链表,那么如果有交点,这个纯环链表必然所有节点都和另一个链表的环部分重合。此时,我们把那个纯环链表的输入节点(假设是head1)看作第一个交点,因此,只需要验证一下head1是否在另一个链表的环上即可。在的话就返回head1,不在就返回NULL。

    因此这道题目其实并不简单,它包含“判断链表是否含有环”,“求环入口节点”,“求两条无环链表的第一个交点” 三个子问题。

    这里给出引申问题,也就是求第一个交点的题目的代码,辅助函数traverse 的功能包含判断是否有环,返回环入口节点(无环则返回NULL),以及求头结点距离环入口节点(有环情况)或者尾节点(无环情况)的长度,这个长度被存在len变量里。

    在判断是否有环的过程中,需要注意单节点环链表的情况。

    ListNode* traverse(ListNode* head, int &len, bool &circled){
        ListNode* p1 = head -> next, *p2 = head -> next;
        int step = 1;
        for(; p2 != NULL; ++step, p1 = p1 -> next, p2 = p2 -> next){
            p2 = p2 -> next;
            ++step;
            if(!p2 || p1 == p2) break;
        }
        if(!p2){    //无环 
            len = step;    //用len记录链表长度 
            circled = false;
            return NULL;
        }else{//有环 
            circled = true; 
            len = 0; //用len记录头结点到环入口距离 
            for(p1 = head; p1 != p2; p1 = p1 -> next, p2 = p2 -> next, ++len);
            return p1;
        }
    }
    
    ListNode* FindFirstCommonListNode(ListNode* head1, ListNode* head2){
        if(!head1 || !head2) return NULL;
        if(head1 == head2) return head1;
        int len1 = 0, len2 = 0; bool cc1 = false, cc2 = false;
        ListNode* CcleEnt1 = traverse(head1, len1, cc1);
        ListNode* CcleEnt2 = traverse(head2, len2, cc2);
        
        if((!cc1 && cc2) || (cc1 && !cc2)) return NULL;    //若一个有环,一个无环,则肯定没有交点
        if(len1 > 0 && len2 > 0){    //当两链表都无环,或者都有环且首节点都不在环上时 
            ListNode *st1 = (len1 > len2 ? head1 : head2);
            ListNode *st2 = (len1 > len2 ? head2 : head1);
            ListNode *cce1 = (len1 > len2 ? CcleEnt1 : CcleEnt2);
            ListNode *cce2 = (len1 > len2 ? CcleEnt2 : CcleEnt1);
            for(int i = 0; i < (len1 - len2 > 0 ? len1 - len2 : len2 - len1); ++i, st1 = st1 -> next);
            for(; st1 != cce1 && st2 != cce2 && (st1 != st2); st1 = st1 -> next, st2 = st2 -> next);
            if(st1) return st1;
            return NULL;
        }else{    //len1, len2 中有一个为0 说明其中至少有一条链表是纯环链表。
            ListNode *st1 = (len1 == 0 ? head1 : head2); //选择那个纯环链表的head,验证它是否在另一个链表的环上,在的话它就是第一个交点,不在的话就没有交点。 
            ListNode *st2 = (len1 == 0 ? head2 : head1);
            ListNode *p = st2 -> next;
            for(; p != st2 && p != st1; p = p -> next);
            if(p == st1) return st1;
            return NULL;
        }
    }

    带测试用例的代码:

    #include <iostream>
    using namespace std;
    
    struct ListNode{
        int val;
        ListNode* next;
        ListNode(int x) : val(x), next(NULL) {};
    };
    
    ListNode* CreateListNode(int value)
    {
        ListNode* pNode = new ListNode(value);
    
        return pNode;
    }
    
    void ConnectListNodes(ListNode* pCurrent, ListNode* pNext)
    {
        if(pCurrent == NULL)
        {
            cout << "Error to connect two nodes.
    ";
        }
    
        pCurrent -> next = pNext;
    }
    
    ListNode* traverse(ListNode* head, int &len, bool &circled){
        ListNode* p1 = head -> next, *p2 = head -> next;
        int step = 1;
        //cout << "---------
    ";
        for(; p2 != NULL; ++step, p1 = p1 -> next, p2 = p2 -> next){    //只有一个节点的环链表 
            //cout << "p2: " << p2 -> val << endl;
            p2 = p2 -> next;
            ++step;
            if(!p2 || p1 == p2) break;
        }
        //cout << "---------
    ";
        if(!p2){    //无环 
            len = step;    //用len记录链表长度 
            circled = false;
            
            cout << "circled: " 
                << (circled ? "Yes" : "No") 
                << "; length: " << len << endl;
                
            return NULL;
        }else{//有环 
            circled = true; 
            len = 0; //用len记录头结点到环入口距离 
            for(p1 = head; p1 != p2; p1 = p1 -> next, p2 = p2 -> next, ++len);
            
            cout << "circled: " 
                << (circled ? "Yes" : "No") 
                << "; length: " << len << endl;
            
            return p1;
        }
    }
    
    ListNode* JudgeCommonListNode(ListNode* head1, ListNode* head2){
        if(!head1 || !head2) return NULL;
        if(head1 == head2) return head1;
        int len1 = 0, len2 = 0; bool cc1 = false, cc2 = false;
        ListNode* CcleEnt1 = traverse(head1, len1, cc1);
        ListNode* CcleEnt2 = traverse(head2, len2, cc2);
        
        if((!cc1 && cc2) || (cc1 && !cc2)) return NULL;    //若一个有环,一个无环,则肯定没有交点
        
        if(len1 > 0 && len2 > 0){    //当两链表都无环,或者都有环且首节点都不在环上时 
            ListNode *st1 = (len1 > len2 ? head1 : head2);
            ListNode *st2 = (len1 > len2 ? head2 : head1);
            ListNode *cce1 = (len1 > len2 ? CcleEnt1 : CcleEnt2);
            ListNode *cce2 = (len1 > len2 ? CcleEnt2 : CcleEnt1);
            for(int i = 0; i < (len1 - len2 > 0 ? len1 - len2 : len2 - len1); ++i, st1 = st1 -> next);
            for(; st1 != cce1 && st2 != cce2 && (st1 != st2); st1 = st1 -> next, st2 = st2 -> next);
            if(st1) return st1;
            return NULL;
        }else{    //len1, len2 中有一个为0 说明其中至少有一条链表是纯环链表。
            ListNode *st1 = (len1 == 0 ? head1 : head2); //选择那个纯环链表的head,验证它是否在另一个链表的环上,在的话它就是第一个交点,不在的话就没有交点。 
            ListNode *st2 = (len1 == 0 ? head2 : head1);
            ListNode *p = st2 -> next;
            for(; p != st2 && p != st1; p = p -> next);
            if(p == st1) return st1;
            return NULL;
        }
    }
    
    
    /**---------测试代码----------*/
    ListNode* CreateList1(){
        ListNode* pNode1 = CreateListNode(1);
        ListNode* pNode2 = CreateListNode(2);
        ListNode* pNode3 = CreateListNode(3);
        ListNode* pNode4 = CreateListNode(4);
        ListNode* pNode5 = CreateListNode(5);
    
        ConnectListNodes(pNode1, pNode2);
        ConnectListNodes(pNode2, pNode3);
        ConnectListNodes(pNode3, pNode4);
        ConnectListNodes(pNode4, pNode5);
        
        return pNode1;
    }
    
    ListNode* CreateCc1(){
        ListNode* pNode1 = CreateListNode(1);
        ListNode* pNode2 = CreateListNode(2);
        ListNode* pNode3 = CreateListNode(3);
        ListNode* pNode4 = CreateListNode(4);
        ListNode* pNode5 = CreateListNode(5);
    
        ConnectListNodes(pNode1, pNode2);
        ConnectListNodes(pNode2, pNode3);
        ConnectListNodes(pNode3, pNode4);
        ConnectListNodes(pNode4, pNode5);
        ConnectListNodes(pNode5, pNode2);
        
        return pNode1;
    }
    
    int main(){
    //    ListNode* head1 = CreateList1();
    //    ListNode* head2 = CreateCc1();
    //    ListNode* res = JudgeCommonListNode(head1, head2);
    
    //    ListNode* pNode1 = CreateListNode(1);
    //    ListNode* pNode2 = CreateListNode(2);
    //    ListNode* pNode3 = CreateListNode(3);
    //    ConnectListNodes(pNode1, pNode2);
    //    ConnectListNodes(pNode3, pNode2);
    //    ListNode* res = JudgeCommonListNode(pNode1, pNode3);
    //    
    //    ListNode* pNode1 = CreateListNode(1);
    //    ListNode* pNode2 = CreateListNode(2);
    //    ConnectListNodes(pNode1, pNode2);
    //    ListNode* res = JudgeCommonListNode(pNode1, pNode2);
    //
    //    ListNode* pNode1 = CreateListNode(1);
    //    ListNode* pNode2 = CreateListNode(2);
    //    ConnectListNodes(pNode1, pNode1);
    //    ConnectListNodes(pNode2, pNode2);
    //    ListNode* res = JudgeCommonListNode(pNode1, pNode2);
    
    //    ListNode* pNode1 = CreateListNode(1);
    //    ListNode* pNode2 = CreateListNode(2);
    //    ListNode* pNode3 = CreateListNode(3);
    //    ListNode* pNode4 = CreateListNode(4);
    //    ListNode* pNode5 = CreateListNode(5);
    //    ListNode* pNode6 = CreateListNode(5);
    //    ConnectListNodes(pNode1, pNode2);
    //    ConnectListNodes(pNode2, pNode3);
    //    ConnectListNodes(pNode3, pNode4);
    //    ConnectListNodes(pNode4, pNode5);
    //    ConnectListNodes(pNode6, pNode3);
    
        ListNode* pNode1 = CreateListNode(1);
        ListNode* pNode2 = CreateListNode(2);
        ListNode* pNode3 = CreateListNode(3);
        ListNode* pNode4 = CreateListNode(4);
        ListNode* pNode5 = CreateListNode(5);
        ListNode* pNode6 = CreateListNode(6);
        ListNode* pNode7 = CreateListNode(7);
        ListNode* pNode8 = CreateListNode(8);
        ConnectListNodes(pNode1, pNode2);
        ConnectListNodes(pNode2, pNode3);
        ConnectListNodes(pNode3, pNode4);
        ConnectListNodes(pNode4, pNode5);
        ConnectListNodes(pNode5, pNode6);
        ConnectListNodes(pNode6, pNode3);
        ConnectListNodes(pNode7, pNode8);
        ConnectListNodes(pNode8, pNode2);
        //ListNode* res = JudgeCommonListNode(pNode1, pNode7);
        
        //ListNode* res = JudgeCommonListNode(pNode7, pNode3);
        ListNode* res = JudgeCommonListNode(pNode1, pNode5);
        
        if(res) cout << "First overlap is: " << res -> val << endl;
        else cout << "Not overlap" << endl;
        
        return 0;
    } 
    带有Main函数的代码
  • 相关阅读:
    IP地址 子网掩码 默认网关和DNS服务器的关系
    ios下微信浏览器如何唤醒app?app已上架应用宝
    iOS: 零误差或极小误差的定时执行或延迟执行?
    iOS单例创建的一点疑惑
    Method Swizzing中一般替换方法都写在Category类别里吗?有没有别的实现方式
    相机拍照友盟检测crash是为什么?
    使用google API之前需要對input 做什麼 安全性的處理?
    关于node的聊天室错误
    Node+Deployd+MongoDB安装问题
    array.fliter无法正确过滤出我想要的数组
  • 原文地址:https://www.cnblogs.com/felixfang/p/3821923.html
Copyright © 2020-2023  润新知