首页 > 编程语言 >链表算法题(下)

链表算法题(下)

时间:2024-09-05 23:53:46浏览次数:11  
标签:slow ListNode fast 链表 算法 节点 指针

链表算法题(上)长中我们已经学习了一系列的链表算法题,那么在本篇中我们将继续来学习链表的算法题,接下来就继续来破解链表的算法题吧!


1.相交链表

160. 相交链表 - 力扣(LeetCode)

通过以上的题目的描述该算法题要我们实现的代码功能是判断两条链表是否相交,如果相较的话就返回相较节点的指针,不相较就返回NULL

那么在实现该算法题的代码之前先要来分析如何实现判断是否是相交链表

首先来看相交链表有什么特征呢?来看以下的示例

从以上的示例就可以看出当两个链表是相交的,那么在这两条链表内一定会有两个节点的下一个节点是相同的。而如果链表不是相交的,那么就不会有两个节点的下一个节点是相同的

当两个链表是相较的时候我们需要返回的就是以上这个节点,那么该如何来找出这个相交的节点呢?

在此我们的解决方法是先定义两个指针变量一开始分别指向两个链表的第一个节点,之后通过各遍历一次两条链表,之后就得到这两条链表各自的节点个数,之后将得到的两个节点数大的减小的得到两链表长度的差值,将节点个数多的链表的定义的节点指针往后走之前得到的差值,最后将两个链表的指针同时往后遍历,当两个指针相同时就说明这两个链表为相交链表,否则就不相交

例如以下示例:

首先A链表个数为5,B节点个数为6,两个链表节点差值就为1

在定义两个指向两个;链表的第一个节点的指针pcur1和pcur2,由于B链表为节点个数多的链表因此将B链表的指针pcur2先向后走1步

之后让pcur1和pcur2同时向后遍历,之后pcur1和pcur2同时指向c1节点,这就可以说明A和B这两个链表是相交链表

接下来我们就来实现该算法题的代码
在以下得到两个链表的节点数差值使用的是库函数abs,该函数能计算出差值的绝对值

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
typedef struct ListNode ListNode;
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) 
{
    ListNode* pcur1=headA;
    ListNode *pcur2=headB;
    
    int count1=0;
    int count2=0;
    while(pcur1)//得到第一个链表的结点数
    {
        count1++;
        pcur1=pcur1->next;
    }
    while(pcur2)//得到第二个链表的节点数
    {
        count2++;
        pcur2=pcur2->next;
    }
    int tmp=abs(count1-count2);//得到两个链表节点数的差值
    ListNode* longlist=headA;
    ListNode* shortlist=headB;
    if(count1<count2)//确定长短链表
    {
        longlist=headB;
        shortlist=headA;
    }
    while(tmp--)//先将长链表走tmp步
    {
        longlist=longlist->next;
    }
    while(longlist && shortlist)//同时遍历两个链表
    {
        if(longlist==shortlist)
        {
            return longlist;
        }
        longlist=longlist->next;
        shortlist=shortlist->next;
    }
    return NULL;
}

2.环形链表I

141. 环形链表 - 力扣(LeetCode)

根据以上的题目描述就可以看出该算法题要我们实现的是判断链表是否为循环链表,在此循环链表是指链表的尾节点不在是连接NULL,而是连接链表中的其中一个节点

那么使用什么方法能判断链表是否为循环链表呢?

在此我们使用的是之前学习过的前后指针法,首先定义有两个指针变量fast和slow,之后fast指针每次走两步,slow指针每次走一步,到最后如果fast指针和slow相遇就说明该链表为循环链表

例如以下示例:

要判断以上链表是否为循环链表我们来看看使用前后指针法的过程是什么样的,首先定义快指针fast和满指针slow,之后让fast和slow遍历链表

 正如上图所示slow指针和fast指针在节点-4相遇,这就说明该链表为循环链表为循环链表

接下来我们就来实现该算法题的代码

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
 typedef struct ListNode ListNode;
bool hasCycle(struct ListNode *head) 
{
    ListNode* fast,*slow;
    fast=slow=head;
    while(fast && fast->next)
    {
        fast=fast->next->next;
        slow=slow->next;
        if(fast==slow)
        {
            return true;
        }
    }
    return false;
}

 

在解决了以上的算法题后我们就要思考为什么快指针每次走两步,慢指针走一步可以相遇,有没有可能遇不上呢?

slow一次走⼀步,fast一次走2步,fast先进环,假设slow也走完入环前的距离,准备进环,此时fast和slow之间的距离为N,接下来的追逐过程中,每追击⼀次,他们之间的距离缩小1步

这时在追击过程中fast和slow之间的距离变化:

因此,在带环链表中慢指针走一步,快指针走两步最终一定会相遇。

 

那么在解决以上问题后思考快指针一次走3步,走4步,...n步行吗?

step1:
按照上面的分析,慢指针每次走一步,快指针每次走三步,此时快慢指针的最大距离为N,接下来的追逐过程中,每追击一次,他们之间的距离缩小2步
追击过程中fast和slow之间的距离变化:

分析:
1、如果N是偶数,第一轮就追上了
2、如果N是奇数,第一轮追不上,快追上,错过了,距离变成-1,即C-1,进入新的一轮追击

   a:C-1如果是偶数,C-1如果是偶数,那么下一轮就追上了
    b:C-1如果是奇数,那么就永远都追不上                          
总结一下追不上的前提条件:N是奇数,C是偶数

 

step2:

在以上的step1中我们得出当fast追不上的前提条件:N是奇数,C是偶数,那么接下来我们就要继续分析这种情况是否会出现

假设:
环的周长为C,头结点到slow结点的长度为L,slow走一步,fast走三步,当slow指针入环后,slow和fast指针在环中开始进行追逐,假设此时fast指针已经绕环x周。
在追逐过程中,快慢指针相遇时所走的路径长度:

fast: L+xC+C-N
slow: L

由于慢指针走一步,快指针要走三步,因此得出: 3 * 慢指针路程 = 快指针路程 ,即:

3L=L+xC+C-N
2L=(x-1)C-N

对上述公式继续分析:由于偶数乘以任何数都为偶数,因此 2L 一定为偶数,则可推导出可能得情
况:

• 情况1:偶数 = 偶数 - 偶数
• 情况2:偶数 = 奇数 - 奇数

由step1中(1)得出的结论,如果N是偶数,则第一圈快慢指针就相遇了。
由step1中(2)得出的结论,如果N是奇数,则fast指针和slow指针在第一轮的时候套圈了,开始进行下一轮的追逐;当N是奇数,要满足以上的公式,则 (x+1)C 必须也要为奇数,即C为奇数,满足(2)a中的结论,则快慢指针会相遇

因此, step1 中的 N是奇数,C是偶数不成立,既然不存在该情况,则快指针一次走3步最终一定也可以相遇。

因此快指针一次走4、5.....步最终也会相遇,其证明方式同上

注:虽然已经证明了快指针不论走多少步都可以满足在带环链表中相遇,但是在编写代码的时候会有额外的步骤引入,涉及到快慢指针的算法题中通常习惯使用慢指针走⼀步快指针走两步的方式。

3.环形链表II

142. 环形链表 II - 力扣(LeetCode)

在以上环形链表I中我们已经实现了如何判断一个链表是否为环形链表,接下来我们在环形链表算法题中我们将跟进一步当链表是环形链表时我们还要返回链表进入环的第一个节点

那么要如何才能找到环形链表进入环的第一个节点呢?接下来我们要来了解一个性质

让一个指针从链表起始位置开始遍历链表,同时让⼀个指针从判环时相遇点的位置开始绕环运
行,两个指针都是每次均走一步,
最终肯定会在入口点的位置相遇

 

例如以下示例:

根据使用快慢指针法我们就可以的到以上链表两指针的相遇节点为-4

在此之后我们再定义一个指针变量pcur指向链表的第一个节点,之后让pcur指针和fast指针同时遍历,当这两个指针指向同一个节点时,指针指向的节点就是入环前的节点

接下来我们就来实现该算法题的代码

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
 typedef struct ListNode ListNode;
struct ListNode *detectCycle(struct ListNode *head)
{
    ListNode* fast,*slow;
    fast=slow=head;
    while(fast&& fast->next)
    {
        fast=fast->next->next;
        slow=slow->next;
        if(slow==fast)
        {
            ListNode* pcur=head;
            while(pcur!=fast)
            {
                pcur=pcur->next;
                fast=fast->next;
            }
            return fast;
        }
    }
    return NULL;
}

在解决了以上的算法题后我们就要证明为什么在环形链表中以上的这种性质

在以上图示当中H为链表的起始点,E为环入口点,M与判环时候相遇点 

在此我们设环的长度为R,H到E的距离为L,E到M的距离为 X ,则:M到E的距离为 R-X

由于快指针和满指针走过的路径分别为
fast: L+X + nR
slow:L+X

又因为快指针走过的路径长度为满指针走过的路径长度的两倍,这时就能得到以下的公式
L+X+nR=2(L+X)
根据以上公式就可以推断出以下公式:
L+X=nR
在此可以继续得到
L=(n-1)R+R-X

当慢指针进入环时,快指针可能已经在环中绕了n圈了,n⾄少为1因为:快指针先进环走到M的位置,,最后又在M的位置与慢指针相遇

所以以上公式n可能为1,2,3,4......,n的大小取决于环的大小,环越小n越大,在此极端情况下,假设n=1,此时: L=R-X

根据L=R-X即可说明⼀个指针从链表起始位置运行,⼀个指针从相遇点位置绕环,每次都走⼀步,两个指针最终会在入口点的位置相遇

标签:slow,ListNode,fast,链表,算法,节点,指针
From: https://blog.csdn.net/2303_81098358/article/details/141870295

相关文章

  • 算法图解(5~6)
    散列表|哈希表(HashTable)通过散列函数将关键字(key)映射到数组中的特定位置,以便来快速查找数据。哈希表就是c++内置散列表散列函数:将输入的关键字转换为整数,散列函数的质量决定了散列表的性能,好的散列函数应能均匀地分布关键字,避免冲突。填充|负载因子:元素总数/位置总......
  • php 实现推荐算法
            在PHP中实现推荐算法的应用场景通常包括电商、社交媒体、内容平台等。推荐算法可以帮助用户找到与其兴趣相关的内容,提高用户体验和平台黏性。以下是几种常见的推荐算法及其PHP实现方式:1.基于协同过滤的推荐算法协同过滤(CollaborativeFiltering)是一种常见的......
  • 旅行商问题 | Matlab基于混合粒子群算法GA-PSO的旅行商问题TSP
    目录效果一览基本介绍建模步骤程序设计参考资料效果一览基本介绍混合粒子群算法GA-PSO是一种结合了遗传算法(GeneticAlgorithm,GA)和粒子群优化算法(ParticleSwarmOptimization,PSO)的优化算法。在解决旅行商问题(TravelingSalesmanProblem,TSP)时,这种混合算法可......
  • codetop算法分类
    以下是按照常见算法标签将题目进行归类的列表:1.动态规划10.正则表达式匹配1143.最长公共子序列115.不同的子序列120.三角形最小路径和121.买卖股票的最佳时机123.买卖股票的最佳时机III131.分割回文串152.乘积最大子数组188.买卖股票的最佳时机IV198.打家......
  • 学习算法需要数学知识吗?
    目录算法与数学:看似不可分割的关系常见算法中的数学元素案例分析:不需要高深数学知识的算法1.二分查找2.深度优先搜索(DFS)3.动态规划:斐波那契数列如何在有限的数学背景下学习算法1.专注于算法的逻辑和过程2.可视化算法流程3.从简单的实现开始,逐步优化4.学......
  • 基于SSA奇异谱分析算法的时间序列趋势线提取matlab仿真
    1.程序功能描述   奇异谱分析(SingularSpectrumAnalysis,简称SSA)是一种强大的非线性和非参数时间序列分析方法。该方法基于奇异值分解(SVD)和轨迹矩阵的概念,用于提取时间序列中的趋势、周期性和噪声成分。在本课题中,通过SSA算法,从强干扰序列中提取其趋势线。2.测试软件版本......
  • 基于鱼群算法的散热片形状优化matlab仿真
    1.课题概述使用浴盆曲线进行空隙外形的模拟,然后通过优化,计算得到最优的浴盆曲线的各个参数,从而计算出最优的R值。浴盆曲线函数如下所示:从上面的仿真结果可知,直接对目标函数进行优化,仿真速度非常慢,这里我们使用浴缸曲线结合鱼群算法进行优化。从而得到最佳的孔隙度值和H对应......
  • 基于鱼群算法的散热片形状优化matlab仿真
    1.课题概述        使用浴盆曲线进行空隙外形的模拟,然后通过优化,计算得到最优的浴盆曲线的各个参数,从而计算出最优的R值。浴盆曲线函数如下所示:          从上面的仿真结果可知,直接对目标函数进行优化,仿真速度非常慢,这里我们使用浴缸曲线结合鱼群算法进......
  • 基于SSA奇异谱分析算法的时间序列趋势线提取matlab仿真
    1.程序功能描述奇异谱分析(SingularSpectrumAnalysis,简称SSA)是一种强大的非线性和非参数时间序列分析方法。该方法基于奇异值分解(SVD)和轨迹矩阵的概念,用于提取时间序列中的趋势、周期性和噪声成分。在本课题中,通过SSA算法,从强干扰序列中提取其趋势线。2.测试软件版本以及......
  • 构建STM32智能平衡车项目:PID控制算法与蓝牙通信技术
    一、项目概述项目目标和用途本项目旨在设计和实现一款基于STM32单片机的平衡车。平衡车是一种新型的个人交通工具,广泛应用于短途出行、休闲娱乐等场景。通过本项目,我们希望能够实现一款具备良好稳定性和操控性的平衡车,能够在不同的地形上自如行驶。解决的问题和带来的价......