《算法通关村第一关-链表经典问题之:两个链表的第一个公共子节点》

描述:输入两个无环的单向链表,找出它们的第一个公共子节点,没有公共节点则返回空。

如图A,B两个单链表,我们需要找出它们的第一个公共子节点6

方法一: 没有其他思路时,我们可能会这么做,暴力循环,将节点一一对比,但时间复杂度高,效率太低,不推荐。

public class Solution {
    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {

        ListNode temp1 = pHead1;
        for (; temp1 != null; temp1 = temp1.next) {

            ListNode temp2 = pHead2;
            for (; temp2 != null; temp2 = temp2.next) {
                if (temp1 == temp2) {
                    return temp1;
                }
            }

        }

        return null;

    }

}

方法二:使用HashSet集合,将其中某一个单链表放到HashSet(不允许有重复元素)中,再将另一个单链表与HashSet中的元素对比,其自带的contains()方法直接调用来判断是否包含该元素,简单高效。同理HashMap也行。

public class Solution {
    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {

        HashSet<ListNode> hs = new HashSet<ListNode>();

        while (pHead1 != null) {
            hs.add(pHead1);
            pHead1 = pHead1.next;
        }

        while (pHead2 != null) {
            if (hs.contains(pHead2))
                return pHead2;
            pHead2 = pHead2.next;
        }

        return null;
    }
}

方法三:栈,将两个单链表全部压入栈中,如果栈顶元素一样则都执行出栈操作,并将出栈后的节点赋值给resultNode,不一样时则停止,最后一个出栈的元素即为公共节点,交给resultNode返回。如果不存在公共节点,没有出栈操作,那么直接返回的resultNode就是null。

public class Solution {
    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {

        Stack<ListNode> s1 = new Stack<>();
        Stack<ListNode> s2 = new Stack<>();


        while (pHead1 != null) {
            s1.push(pHead1);
            pHead1 = pHead1.next;
        }

        while (pHead2 != null) {
            s2.push(pHead2);
            pHead2 = pHead2.next;
        }

        ListNode resultNode = null;
        while (s1.size() > 0 && s2.size() > 0) {

            if (s1.peek() == s2.peek()) {
                resultNode = s1.pop();
                s2.pop();

            } else {
                break;
            }

        }

        return resultNode;
    }
}

可以参考这张图,理解一下

 方法四:拼接两个单链表,由图可知,拼接后的AB,BA链表一定会出现相同的部分,从最后的6开始。我们只需要一边遍历两个拼接后的链表,一边进行比较,就能找到公共节点。

由于新建立链表太浪费空间了,我们可以优化一下。只要每次遍历完一个链表之后,调整一下指针到下一个链表的表头就行了。比如:我们访问完了A之后将指针指向B的表头,再将B遍历完,不就相当于是遍历完了AB。

public class Solution {
    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {

        if (pHead1 == null || pHead2 == null) {
            //如果两个单链表中的一个或全部为空,则不可能存在公共节点,直接返回null
            return null;
        }

        ListNode temp1 = pHead1;
        ListNode temp2 = pHead2;

        while (temp1 != temp2) {
            temp1 = temp1.next;
            temp2 = temp2.next;

//这个判断条件很重要,如果两个单链表不存在公共节点,那么执行到最后,temp1和temp2都等于null,它们相等了就不会执行下面的语句,同时也会结束while循环,避免了死循环
            if (temp1 != temp2) {

//如果第一个链表遍历完了将指针指向第二个的表头
                if (temp1 == null) {
                    temp1 = pHead2;
                }
//如果第二个链表遍历完了将指针指向第一个的表头
                if (temp2 == null) {
                    temp2 = pHead1;
                }

            }

        }


        return temp1; 
        //返回temp1或temp2中的那个都行,如果两个单链表不存在公共节点,两个都为null,则返回null。存在公共节点则它们都等于公共节点。
    }
}

 

方法五:通过两个单链表的长度差求公共节点,首先我们分别求出两个单链表的长度设La和Lb,然后我们求| La-Lb |的差值,让长的那个单链表先走| La-Lb |步,最后再一起遍历两个单链表,就能找到公共节点了。

public class Solution {
    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
        if (pHead1 == null || pHead2 == null) {
            return null;
        }

        int l1 = 0, l2 = 0;
        ListNode current1 = pHead1;
        ListNode current2 = pHead2;

        while (current1 != null) {
            current1 = current1.next;
            l1++;
        }
        while (current2 != null) {
            current2 = current2.next;
            l2++;
        }

        int sub = (l1 > l2) ? l1 - l2 : l2 - l1;

        if (l1 > l2) {
            for (int a = 0; a < sub; a++) {
                pHead1 = pHead1.next;
            }

        } else {
            for (int a = 0; a < sub; a++) {
                pHead2 = pHead2.next;
            }
        }

        while (pHead1 != pHead2) {
            pHead1 = pHead1.next;
            pHead2 = pHead2.next;
        }


        return pHead1;
    }
}

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值