算法通关村第一天——链表经典问题首个公共子节点

                题目:输入两个链表,找到第一个公共子节点,例如下面两个链表:

算法通关村第一天——链表经典问题首个公共子节点_第1张图片

        两个链表的头结点是已知的,相交之后形成一个单链表,但相交位置是未知的 ,相交之前节点数也是未知的,求相交点。

没有思路怎么解题:将常用的数据结构和算法思想都想一遍,看看那个能解决问题,

回顾一下

常用的数据结构有数组、链表、队、栈、Hash、集合、树、堆。

常用的算法思想有查找、排序、双指针、递归、迭代、分治、贪心、回溯和动态规划等待。

冒泡排序,实现简单但复杂度高,排除!

Hash,将第一个链表存到Hash中,然后一边遍历第二个链表,一边检测当前元素是否存在Hash中,如果有交点,就会找到,集合同理。

栈,将两个链表分别压入栈中,因为存在交点,后部分为相同的单链表(图的相交点之后),所以后压入的结点会有一段相等的加点,然后依次弹出,比较两个结点是否一致,队后出栈的一组一致结点就是要找的位置。

面试中可以将所有思路跟面试官讲,看面试官想要那种,即使错了,也可以重新思考,甚至还会提醒,这个我们要记得。

1.1 哈希和集合

        一个链表存入栈中遍历另一个链表进行比较,有交点则一定能检测出来,使用集合代码会简便,看代码:注意代码注释

首先规范LIstNode类

public class ListNode {
    private int val;
    ListNode next;

  ListNode(int x){
      val = x;
      next = null;
  }
}
    public ListNode find(ListNode headA ,ListNode headB){
        HashSet set = new HashSet<>();
        //将A链表存入集合中
        while (headA  != null){
            set.add(headA);
            headA = headA.next;
        }
        //遍历B和集合中元素比较
        while(headB != null){
            if (set.contains(headB)){
                return headB;
            }
            headB = headB.next;
        }
        return null;
    }

1.2 使用栈

        将两个链表分别入栈,然后分别出栈,如果相等就继续出栈,一直找到最晚出栈的一组就是要找的交点,需要两个O(n)的空间。

代码:

    public ListNode stack(ListNode headA,ListNode headB){
        //1.创建两个空栈,用来存储元素
        Stack stackA = new Stack();
        Stack stackB = new Stack();
        //2.分别压入栈中
        while(headA != null){
            stackA.push(headA);
            headA = headA.next;
        }

        while(headB != null){
            stackB.push(headA);
            headB = headB.next;
        }
        //3.分别出栈然后比较,
        //最后一个弹出的结点
        ListNode preNode = null;
        //判断条件,栈空
        while(stackB.size() >0 && stackA.size()>0){
            //peek()查看栈顶,pop()弹出并打印
            if (stackA.peek() == stackB.peek()){
                preNode = stackA.pop();
                stackB.pop();
            }else {
                //此时交点之后结点全部弹出
                break;
            }
        }
            return preNode;
    }

1.3 拼接两个字符串

A : 0-1-2-3-4-5

B : a-b-4-5

拼接之后:

AB:0-1-2-3-4-5-a-b-4-5

BA :  a-b-4-5-0-1-2-3-4-5

        发现从4之后开始两个结点一样了,4就是要找的结点,以交点为中心,将链表分别分为left_a和right_a,left_b和right_b,此处有点难以理解,看图:

算法通关村第一天——链表经典问题首个公共子节点_第2张图片

 所以找到最后的4结点就好,两个链表轮流遍历,代码:

    //pHead1和pHead2为AB和BA的头
    public ListNode find(ListNode pHead1, ListNode pHead2) {
        if (pHead1 == null || pHead2 == null) {
            return null;//排除空链表
        }
        ListNode p1 = pHead1;
        ListNode p2 = pHead2;
        //两者同时遍历
        while (p1 != p2) {
            p1 = p1.next;
            p2 = p2.next;
            //如果这里不判断,没有公共节点的时候,一条链表遍历完会直接切换另一条链表 ,直接切换没有终止条件,(例如都遍历到最后又会重新遍历)
            if (p1 != p2) {
                //一个链表访问完了就跳到另一个链表访问,此时说的是A或者B
                if (p1 == null) {
                    p1 = pHead2;
                }
                if (p2 == null) {
                    p2 = pHead1;
                }
            }
        }
        return p1;
    }

1.4 差和双指针

        第一轮遍历,假设La长度L1,Lb长度为L2,则|L2 - L1| 就是两个结点的差值,第二轮遍历,长的先走|L2 - L1|,然后两个结点同时向前走,结点一样时候就是公共结点,(简单理解,同时遍历的时候,在交点之前,长的会多走一点,以致于错过一点,短的会先经过交点,计算出来多走的,第二轮长的先走一点,两者就会同时到达公共点)。

代码实现:

public ListNode find(ListNode pHead1, ListNode pHead2) {
        if (pHead1 == null || pHead2 == null) {
            return null;
        }
        ListNode current1 = pHead1;
        ListNode current2 = pHead2;
        int l1 = 0, l2 = 0;
        //分别统计两个链表长度
        while (current1 != null){
            current1 = current1.next;
            l1++;
        }while (current2 != null){
            current2 = current2.next;
            l2++;
        }
        
        current1 = pHead1;
        current2 = pHead2;
        int sub = l1-l2>0?l1-l2:l2-l1;
        //长的先走sub步
        if(l1 >l2){
            int a = 0;
            while (a

你可能感兴趣的:(算法通关村专栏,算法,链表,数据结构)