数据结构_链表_经典题目_两个链表的第一个公共节点

0. 相关文章

1. 题目

牛客网链接

输入两个无环的单向链表,找出它们的第一个公共结点,如果没有公共节点则返回空。(注意因为传入数据是链表,所以错误测试数据的提示是用其他方式显示的,保证传入数据是正确的)

数据范围: n \le 1000n≤1000
要求:空间复杂度 O(1)O(1),时间复杂度 O(n)O(n)

例如,输入{1,2,3},{4,5},{6,7}时,两个无环的单向链表的结构如下图所示:

可以看到它们的第一个公共结点的结点值为6,所以返回结点值为6的结点。
在这里插入图片描述
输入描述:
输入分为是3段,第一段是第一个链表的非公共部分,第二段是第二个链表的非公共部分,第三段是第一个链表和第二个链表的公共部分。 后台会将这3个参数组装为两个链表,并将这两个链表对应的头节点传入到函数FindFirstCommonNode里面,用户得到的输入只有pHead1和pHead2。

返回值描述:
返回传入的pHead1和pHead2的第一个公共结点,后台会打印以该节点为头节点的链表。

在这里插入图片描述

2. 题解

此题有多种解法,由于暴力解法时间复杂度太高,所以此处我们不考虑。

2.1 题解1:使用Hash

  1. 我们先将链表1的所有节点都存储到一个HashSet中。
  2. 遍历链表2的节点,找到第一个在HashSet中存在的节点,即为两个链表的第一个公共节点。
import java.util.*;
/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
        return findByHash(pHead1, pHead2);
    }

    private ListNode findByHash(ListNode pHead1, ListNode pHead2) {
        if (pHead1 == null || pHead2 == null) {
            return null;
        }

        // 1. 将pHead1的所有节点添加进HhashSet中
        Set<ListNode> set = new HashSet<>();
        ListNode tempNode = pHead1;
        while (tempNode != null) {
            set.add(tempNode);
            tempNode = tempNode.next;
        }

        // 2. 遍历pHead2,找到第一个在Set集合中的节点,即为第一个公共节点
        tempNode = pHead2;
        while (tempNode != null) {
            if (set.contains(tempNode)) {
                return tempNode;
            }
            tempNode = tempNode.next;
        }
        return null;
    }
}

2.2 题解2:使用栈

  1. 将两个链表分别压入两个栈中
  2. 比较栈顶元素,如果相同,即为该节点的公共节点。
  3. 因此我们将相同的节点同时出栈,找到的最后一个相同的节点,即为第一个公共子节点。
import java.util.*;
/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
        return findByStack(pHead1, pHead2);
    }

    private ListNode findByStack(ListNode pHead1, ListNode pHead2) {
        // 1. 将两个链表存入两个栈中
        Stack<ListNode> stack1 = new Stack<>();
        Stack<ListNode> stack2 = new Stack<>();
        ListNode tempNode1 = pHead1;
        while (tempNode1 != null) {
            stack1.push(tempNode1);
            tempNode1 = tempNode1.next;
        }
        ListNode tempNode2 = pHead2;
        while (tempNode2 != null) {
            stack2.push(tempNode2);
            tempNode2 = tempNode2.next;
        }

        // 2. 同时出栈,找到第最后一组相同节点,即为第一个公共节点
        ListNode prefNode = null;
        while (!stack1.isEmpty() && !stack2.isEmpty()) {
            if (stack1.peek() != stack2.peek()) {
                return prefNode;
            }
            prefNode = stack1.peek();
            stack1.pop();
            stack2.pop();
        }
        return prefNode;
    }
}

2.3 题解3:通过拼接序列

如果对此题没有思路时,我们肯定会产生一个想法:如果这两个链表一样长就好了。就如下图所示,如果两个链表一样长,我们只需要同时遍历"链表1"和"链表2",找到的第一个相同的节点就是答案。
在这里插入图片描述

public static ListNode findFirstCommonNodeByCombine(ListNode pHead1, ListNode pHead2) {
    while (pHead1 != null && pHead2 != null) {
        if (pHead1 == pHead2) {
            return pHead1;
        }
       
        pHead1 = pHead1.next;
        pHead2 = pHead2.next;
    }
}

但是现在的问题是两个链表的长度不同,如题干中所示链表
在这里插入图片描述

那么如果将两个链表的长度变得相同呢,很简单:我们只需要将链表分别拼接起来即可,如下图所示
在这里插入图片描述

此时我们使用上面的方法,去遍历两个链表,就可以在倒数第二个节点,找到他们的第一个公共节点(6)

import java.util.*;
/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
        return findFirstCommonNodeByCombine(pHead1, pHead2);
    }

    public static ListNode findFirstCommonNodeByCombine(ListNode pHead1, ListNode pHead2) {
        if (pHead1 == null || pHead2 == null) {
            return null;
        }

        ListNode tempNode1 = pHead1;
        ListNode tempNode2 = pHead2;
        // 如同上图所示,拼接后两个链表长度相同,同步遍历时肯定同时走到尾节点
        // 如果此时都没有因找到公共节点而退出,则表明无公共节点
        while (tempNode1 != null || tempNode2 != null) {
            // 如果一个链表为null,则表明该链表已经走到链表尾部
            // 此时将另一个链表拼接上去继续遍历
            if (tempNode1 == null) {
                tempNode1 = pHead2;
            }
            if (tempNode2 == null) {
                tempNode2 = pHead1;
            }

            // 如果节点相同直接返回,否则同时遍历两条链表下一个节点。
            if (tempNode1 == tempNode2) {
                return tempNode1;
            }
            tempNode1 = tempNode1.next;
            tempNode2 = tempNode2.next;
        }
        return null;
    }
}

2.4 题解4:通过差辅助查找

在题解三中,我们可以知道,只需要让两个链表的长度相等,那么我们便可以很轻松的找到他们的第一个公共节点。
因此:我们可以先求出两个链表的长度,长链表先走n(长度差)步,剩余的链表长度就相等了。
以此题为栗子:

  1. 遍历链表,分别求出两条链表的长度(链表1=5;链表2=4)
  2. 长度较长的链表(链表1)先向后遍历1步(5-4=1)
  3. 此时的链表为(链表1=2,3,6,7;链表2=4;5;6;7)
  4. 此时链表1和链表2长度相同,只需要同步遍历便能获取结果。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值