力扣160 - 相交链表【双指针妙解】

简介: 对应力扣160 - 相交链表,双指针妙解两个链表的交点

@TOC

一、题目描述

原题传送门
在这里插入图片描述

示例 1:
在这里插入图片描述

输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,6,1,8,4,5], skipA = 2, skipB = 3
输出:Intersected at '8'
解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,6,1,8,4,5]。
在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。
— 请注意相交节点的值不为 1,因为在链表 A 和链表 B 之中值为 1 的节点 (A 中第二个节点和 B 中第三个节点) 是不同的节点。换句话说,它们在内存中指向两个不同的位置,而链表 A 和链表 B 中值为 8 的节点 (A 中第三个节点,B 中第四个节点) 在内存中指向相同的位置。

示例 2:

在这里插入图片描述

输入:intersectVal = 2, listA = [1,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
输出:Intersected at '2'
解释:相交节点的值为 2 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [1,9,1,2,4],链表 B 为 [3,2,4]。
在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。

示例 3:
在这里插入图片描述

输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2
输出:null
解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。
由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。
这两个链表不相交,因此返回 null 。

提示:

  • listA 中节点数目为 m
  • listB 中节点数目为 n
  • 1 <= m, n <= 3 * 104
  • 1 <= Node.val <= 105
  • 0 <= skipA <= m
  • 0 <= skipB <= n
  • 如果 listA 和 listB 没有交点,intersectVal 为 0
  • 如果 listA 和 listB 有交点,intersectVal == listA[skipA] == listB[skipB]
看完了上面这些题目的描述,相信很多同学都被此题吓住了,觉得这个题目很难,自己做不出来,但是进到原题中可以看到这是一道力扣上的简单题,正确率也不低,有65%左右,所以不要害怕,我带你来慢慢分析一下,如何去求解本题:hourglass:

二、思路分析与罗列

  • 让我猜猜,若是没有题目给出的图示你是不是认为两个相交的链表应该是这样的:smiley:
  • 哈哈,其实我也有想到过,但是后来一想这个只是单链表,到结点的相交处怎么指向后面的两个方向呢,所以果断丢弃了这个思想
在这里插入图片描述
  • 就像下面这样,因为交点处的结点时只有一个next,两个相交的链表只是一个普通的单链表,因此是不会存在下面这种结构的
在这里插入图片描述
  • 但是呢,实际上它是这样的,呈现一个【Y】字形
在这里插入图片描述
  • 以上这些是我在没看本题前的一个思考过程,这个点其实也挺重要的,对于一道题目,每个人都有它自己的思考过程,也不是在一开始就会有思路。==刷题的时候我们应该开动自己的大脑,把自己想到的画出来,试试看,行不行得通,若是行不通,就立马换其他思路,在这个思考的过程中,你对这道题也有了一个逐步的认识==

  • 好了,废话不多说,我们正式开始分析本题的思路
  • 以下是我对于这一道题的思路罗列

在这里插入图片描述

好,看完题目的描述,我们来分析一下去求解这道题目,

在这里插入图片描述

  • 首先我们从案例一可以看到,对于两个链表的相交,就是当两个链表向后遍历的时候会碰到重合的结点,我们可以根据这个特点先对链表做一个遍历。先看看他们是否相交
在这里插入图片描述
  • 我的思路是这样的:首先一点是不能动两个链表的头,采用【curA】和【curB】去代替遍历,遍历结束的条件就是这两个指针的next指向NULL。
  • 在他们都遍历结束后对两个指针进行一个判断,若是相同,则表示两个链表一定在某一处汇聚到一起了,若是不同则表示两个链表不相交。这就是第一步,我们来看看代码
ListNode* curA = headA, *curB = headB;      //遍历
int lenA = 0, lenB = 0;     //记录两个链表的长度
while(curA->next){
    lenA++;
    curA = curA->next;
}

while(curB->next){
    lenB++;
    curB = curB->next;
}

if(curA != curB)
    return NULL;        //若两个结点的地址不同,则表示不相交,return NULL
  • 可以看到在循环内部出现了【lenA】和【lenB】,这个是用来在遍历链表时记录这个链表的长度,我们后面马上会用到

  • 接着第二步,我们就要开始考虑如何去求这两个链表的交点,但是在这之前呢,还要做一步,就是求出较长的那个链表,因为我们无法知晓较长的那个链表是什么,因此在后面求交点时就不知道需要遍历多少次才能碰到交点,所以这个时候我==作出一个假设==:
  • 假设链表A是长的那个,链表B是短的那个。但这始终是我们的假设,并不成立,所以此时需要使用到我上面求出的【lenA】和【lenB】,对他们作出一个比较,若是【lenB > lenA】,则表示我们的假设错误,更新一下长的链表和短的链表,若是【lenA > lenB】,则无需进行判断,表示我们假设成功,直接使用我们一开始假设好的就行。代码是这样的
ListNode* longer = headA, *shorter = headB;      //先假设链表A长于链表B
if(lenB > lenA){            //若是假设错误则交换
    longer = headB;
    shorter = headA;
}
  • 接着我们便求出了较长的那个链表,此时只需要求出两个链表的长度差,记为【gap】。然后让长的那一个链表先走【gap】步,就可以让两个链表在同一起跑线上了。代码如下
int gap = abs(lenA - lenB);        //求出两个链表的长度差
while(gap--)
    longer = longer->next;      //先让长的链表走gap步,使得两链表位于同一起跑线
在这里插入图片描述

  • 完成了前面两步,最后一步我们就可以从两个链表现在的起始位置开始遍历,然后判断他们在中途是否相交与一个结点,此时直接return 就是交点
while(longer != shorter)        //一同往后走,寻找两链表的交点(此时一定相交) 
{
    longer = longer->next;
    shorter = shorter->next;
}
return longer;
在这里插入图片描述

三、整体代码展示

展示一下整体的代码,不过还是自己去敲一遍比较好哦
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        ListNode* curA = headA, *curB = headB;      //遍历
        int lenA = 0, lenB = 0;     //记录两个链表的长度
        while(curA->next){
            lenA++;
            curA = curA->next;
        }

        while(curB->next){
            lenB++;
            curB = curB->next;
        }

        if(curA != curB)
            return NULL;        //若两个结点的地址不同,则表示不相交,return NULL

        ListNode* longer = headA, *shorter = headB;      //先假设链表A长于链表B
        if(lenB > lenA){            //若是假设错误则交换
            longer = headB;
            shorter = headA;
        }
            
        int gap = abs(lenA - lenB);        //求出两个链表的长度差
        while(gap--)
            longer = longer->next;      //先让长的链表走gap步,使得两链表位于同一起跑线

        while(longer != shorter)        //一同往后走,寻找两链表的交点(此时一定相交) 
        {
            longer = longer->next;
            shorter = shorter->next;
        }
        return longer;
    }
};

四、总结与提炼

  • 最后我们来总结一下本文所介绍的内容,本文讲解的是一道力扣中有关求解两个链表的相交结点的题目,对于本题虽然看上去比较复杂,但是经过我们的一顿分析之后,你是不是觉得本题并不是很难,因为有了这么一个分析的过程,其实你已经开始思考了,随着一步步解决方案的罗列,也就有了思路,此时再将每一步的思路慢慢转化为代码,其实做题也没有那么难,难的只是这个分析的过程罢了:smile:
以上就是本文所要描述的所有内容,感谢您对本文的观看,如有疑问请于评论区留言或者私信我都可以:four_leaf_clover:
相关文章
|
2月前
|
算法
LeetCode第24题两两交换链表中的节点
这篇文章介绍了LeetCode第24题"两两交换链表中的节点"的解题方法,通过使用虚拟节点和前驱节点技巧,实现了链表中相邻节点的交换。
LeetCode第24题两两交换链表中的节点
|
2月前
|
存储 算法
LeetCode第86题分隔链表
文章介绍了LeetCode第86题"分隔链表"的解法,通过创建两个新链表分别存储小于和大于等于给定值x的节点,然后合并这两个链表来解决问题,提供了一种简单易懂且操作原链表的解决方案。
LeetCode第86题分隔链表
|
2月前
|
存储 算法
LeetCode第83题删除排序链表中的重复元素
文章介绍了LeetCode第83题"删除排序链表中的重复元素"的解法,使用双指针技术在原链表上原地删除重复元素,提供了一种时间和空间效率都较高的解决方案。
LeetCode第83题删除排序链表中的重复元素
|
2月前
|
算法
LeetCode第23题合并 K 个升序链表
这篇文章介绍了LeetCode第23题"合并K个升序链表"的解题方法,使用分而治之的思想,通过递归合并链表的方式解决了这个难题。
LeetCode第23题合并 K 个升序链表
|
2月前
|
C++ 索引
leetcode 707.设计链表
本文提供了解决LeetCode 707题"设计链表"的C++实现,包括单链表的节点定义和类方法实现,如添加节点、获取节点值、删除节点等。
|
2月前
|
算法
LeetCode第92题反转链表 II
文章分享了LeetCode第92题"反转链表 II"的解法,通过使用四个指针来记录和更新反转链表段的头部、尾部以及前一个和后一个节点,提供了一种清晰且易于理解的解决方案。
LeetCode第92题反转链表 II
|
2月前
|
算法
LeetCode第21题合并两个有序链表
该文章介绍了 LeetCode 第 21 题合并两个有序链表的解法,通过创建新链表,依次比较两个链表的头节点值,将较小的值插入新链表,直至其中一个链表遍历完,再将另一个链表剩余部分接到新链表后面,实现合并。
LeetCode第21题合并两个有序链表
|
2月前
|
算法
LeetCode第19题删除链表的倒数第 N 个结点
该文章介绍了 LeetCode 第 19 题删除链表的倒数第 N 个结点的解法,通过使用快慢双指针,先将快指针移动 n 步,然后快慢指针一起遍历,直到快指针到达链尾,从而找到倒数第 N 个结点的前一个结点进行删除,同时总结了快慢指针可减少链表遍历次数的特点。
LeetCode第19题删除链表的倒数第 N 个结点
|
2月前
|
存储 算法 数据处理
指针与链表
指针与链表
53 0