算法:求两个单向链表的最早公共交点

链接:https://mp.weixin.qq.com/s/A4jjclVpd7Q03yJfARR3DA

公众号:程序员架构进阶

一 题目

   求两个单向链表的最早公共交点;如果没有返回null。

二 解析

   链表是单向链表,即只有指向下一个节点的指针,而没有反向;公共节点,指地址相同的节点。即假设链表L1中有一个节点node1,L2中有一个节点node2,node1 = node2,注:这里的“=”,指的是node1 和 node2是同一个节点,也就是说,L1 和 L2都持有了对一个节点的引用,那么就说两个节点实际上是一个公共节点。我们可以用下面的图来表示:

图片

 

上图中的node2和node3就是公共节点,node2是最早公共节点。

链表L1的长度m,链表L2的长度为n。

 

三 算法设计

3.1 多次遍历

   两个链表都是有限长度,最直接的方法,就是直接遍历。从链表L1的第一个节点开始,遍历L2的所有节点,判断L1的这个节点是否与L2中的某个节点是公共节点,如果是,则直接返回这个节点即可;如果遍历结束后发现没有找到,那么返回nul.l。

   时间复杂度:比较次数为mxn,所以时间复杂度为O(mxn);

   空间复杂度:只使用一个临时变量,空间复杂度为O(1)。

3.2 倒序查找

   上面的算法虽然能够找到公共节点,但显然效率太低。我们再看一下公共节点的定义,如果节点node是两个链表的公共节点,那么一定有L1从node开始之后,与L2的node及之后的节点完全相同。

   基于这样的分析,我们从尾部向前查找就可以了,如果最后一个节点是公共节点,那么就继续向前查找,直到找到第一个公共节点。

   但这里还有一个问题,因为给定的是两个单向链表,所以不能直接做倒序查找(前一个节点),所以我们需要处理一下。链表不可以,数组是可以的,所以思路为:

1、链表转数组,得到两个节点数组;

2、从两个数组的最后一个节点开始逐个向前比对,直到找到第一个公共节点位置。

示意如下:

图片

时间复杂度:需要分别遍历一次链表,复杂度为m+n,之后从尾部遍历查找一次,所以时间复杂度为O(m+n+max(m, n));

空间复杂度:需要使用两个数组存储节点,还有一个指针临时变量,空间复杂度为O(m+n)。

3.3 首位对齐,指针逐个后移

   再次分析,如果我们把两个链表按照尾部对齐,第一个公共节点一定出现在从后向前找的第k个节点,那么这个节点一定是链表L1的第m-k个节点,L2的第n-k个节点。

   也就是说,假设m>n,那么我们直接从L1的第(m-n)个节点开始,与链表L2的第1个节点开始对比,如果相同,说明这个节点就是最早公共节点;如果不是,那么两个链表同时向后一位进行对比,判断是否是公共节点,如此下去。公共节点一定在我们比对的这些对节点之中。

图片

 

这种方式下,如果两个链表的长度m,n是已知的,那么直接遍历就可以了,时间复杂度为O(min(m, n));

如果长度未知,那么我们需要遍历一次两个链表,得到两个链表的长度,然后再设置指针的起始位置并进行遍历,复杂度为O(m+n+min(m, n))。

空间复杂度:因为只需要m, n, 指针三个临时变量,所以空间复杂度为O(1)。

四 总结

   这是链表题中并不复杂的一道,如果在leetcode中,应该最多只属于中等难度。但从这道题中,我们仔细思考之后可以看出一些题目之外的东西。

   做题的人看到的是完全相同的信息,但能给出的解答是不同的。也就是说,每个人对信息的理解、提取、利用的能力存在差异,导致会有部分人得不到最优的解答。

    其中一个比较容易犯的错误,或者说容易陷入的思维屏障,就是把题目中的某些条件,只作为了“目标”,而没有发现这本来就是可以用来利用的“条件”。以本题为例,公共节点本身有一定的要求,找到最早公共节点是我们的目标,但同时,公共节点本身的特性也是我们可以用来减少对比次数的条件。
    算法题大多如此,充分利用题目中隐含的所有条件,才可以节约大量的时间或空间,这种思路,在工程中也一样可能适用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值