剑指offer面试题52(java版):两个链表的第一个公共节点

welcome to my blog

剑指offer面试题52(java版):两个链表的第一个公共节点

题目描述

输入两个链表,找出它们的第一个公共结点

思路

  • 两个节点相等,是指针指向的引用相等,不仅仅是节点的值相等
  • 比较容易想到的是从后往前比,找到最后一个相同的节点,缺点是需要辅助空间
  • 书上给的思路更好,从前往后比,不需要辅助空间
    • 先找出两个链表的长度m,n
    • 不妨假设m>n, 先让第一个链表的头指针向后移动(m-n)个节点,这样两个链表便可以同时走到末尾
    • 移动过程中第一个公共节点就是结果
    • 特殊情况:第一个链表包含第二个链表; 没有公共节点

第三次做, 分解问题:1)是否有环 2)寻找第一个公共节点; 让curr1指向长链表的做法要习惯; 初始化→(执行→更新)^n

/*
问题分解:
先判断有没有环
再找公共节点
*/
public class Solution {
    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
         if(pHead1==null || pHead2==null)
             return null;
        ListNode loop1 = hasLoop(pHead1), loop2 = hasLoop(pHead2);
        if(loop1==null && loop2==null)
            return noLoopIntersect(pHead1, pHead2);
        if(loop1!=null && loop2!=null)
            return hasLoopIntersect(pHead1, pHead2, loop1, loop2);
        return null;
    }
    public ListNode hasLoopIntersect(ListNode head1, ListNode head2, ListNode loop1, ListNode loop2){
        ListNode curr1=head1, curr2=head2;
        //环有一个触角
        if(loop1==loop2){
            int n=0;
            while(curr1!=loop1){
                n++;
                //update
                curr1=curr1.next;
            }
            while(curr2!=loop1){
                n--;
                //update
                curr2=curr2.next;
            }
            //curr1指向长链表
            curr1 = n>0? head1 : head2;
            //curr2指向短链表
            curr2 = curr1==head1? head2:head1;
            n = Math.abs(n);
            while(n>0){
                curr1 = curr1.next;
                //
                n--;
            }
            while(curr1!=loop1){
                if(curr1==curr2)
                    return curr1;
                //
                curr1 = curr1.next;
                curr2 = curr2.next;
            }
            return loop1;
        }
        //环有两个触角或者是两个独立的环
        else{
            curr1 = loop1;
            curr2 = loop2;
            while(curr1!=loop1){
                if(curr1==curr2)
                    return curr1;
                curr1=curr1.next;
            }
            return null;
        }
    }
    
    public ListNode noLoopIntersect(ListNode head1, ListNode head2){
        int len1=0, len2=0;
        //初始化→(执行→更新)^n
        ListNode curr1=head1, curr2=head2;
        while(curr1!=null){
            len1++;
            //update
            curr1 = curr1.next;
        }
        while(curr2!=null){
            len2++;
            //update
            curr2 = curr2.next;
        }
        curr1=head1;
        curr2=head2;
        if(len1 > len2){
            for(int i=0; i<len1-len2; i++)
                curr1=curr1.next;
        }
        else
            for(int i=0; i<len2-len1; i++)
                curr2=curr2.next;
        //together move
        while(curr1!=null){
            if(curr1==curr2)
                return curr1;
            //update
            curr1=curr1.next;
            curr2=curr2.next;
        }
        return null;
    }
    
    public ListNode hasLoop(ListNode head){
        if(head==null)
            return head;
        ListNode left=head, right=head.next;
        while(left != right){
            //execute
            if(right==null || right.next==null)
                return null;//no loop
            //update
            left = left.next;
            right = right.next.next;
        }
        return left;
    }
}

第二次做,关键:先统计两个链表的长度

  • 第二遍移动某个指针之前忘记先将两个指针指向链表开头了,导致其中一个指针仍是null
/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
        /*
        先计算两个链表的长度,目的是为了让两个指针距离链表结尾的距离一样
        这样如果有公共节点的话,两个指针总会相遇
        */
        if(pHead1==null || pHead2==null)
            return null;
        int len1=0, len2=0;
        ListNode p1=pHead1, p2=pHead2;
        //统计链表长度
        while(p1!=null){
            len1++;
            p1=p1.next;
        }
        while(p2!=null){
            len2++;
            p2=p2.next;
        }
        //将两个指针重新指向头结点,接着根据链表长度决定移动哪个指针
        p1=pHead1;
        p2=pHead2;
        if(len1>len2){
            p1=pHead1;
            for(int i=0; i<len1-len2; i++)
                p1 = p1.next;
        }
        else{
            p2=pHead2;
            for(int i=0; i<len2-len1; i++)
                p2 = p2.next;
        }
        //两个指针一起后移,寻找第一个公共节点
        while(p1!=null){
            if(p1==p2)
                return p1;
            else{
                p1=p1.next;
                p2=p2.next;
            }
        }
        return null;
    }
}
public class Solution {
    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
         //input check 
        if(pHead1==null || pHead2==null)
            return null;
        //execute
        int len1=1, len2=1;
        ListNode p1=pHead1, p2=pHead2;
        while(p1.next!=null){
            len1++;
            p1 = p1.next;
        }
        while(p2.next!=null){
            len2++;
            p2 = p2.next;
        }
        p1 = pHead1;
        p2 = pHead2;
        if(len1 > len2)
            for(int i=0; i<len1-len2; i++)
                p1 = p1.next;
        else if(len1 < len2)
            for(int i=0; i<len2-len1; i++)
                p2 = p2.next;
        while(p1 != null){
            if(p1 == p2)
                return p1;
            p1 = p1.next;
            p2 = p2.next;
        }
        return null;
    }
}
/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/

超强的代码(leetcode上的)

class Solution {
public:
    ListNode* FindFirstCommonNode( ListNode *pHead1, ListNode *pHead2) {
        ListNode *p1 = pHead1;
        ListNode *p2 = pHead2;
        while(p1!=p2){
            p1 = (p1==NULL ? pHead2 : p1->next);
            p2 = (p2==NULL ? pHead1 : p2->next);
        }
        return p1;
    }
};

思路

  • 断点运行了才完全想明白,好腻害的代码
  • 长度相同有公共结点,第一次就遍历到;没有公共结点,走到尾部NULL相遇,返回NULL
  • 长度不同有公共结点,第一遍差值就出来了,第二遍一起到公共结点;没有公共,一起到结尾NULL

你可能感兴趣的:(剑指offer,剑指offer)