【数据结构】6道经典链表面试题

目录

1.返回倒数第K个节点【链接】

​代码实现

2.链表的回文结构【链接】

代码实现 

3.相交链表【链接】

代码实现

4.判断链表中是否有环【链接】

代码实现

常见问题解析 

5.寻找环的入口点【链接】

代码实现1

​代码实现2

6.随机链表的复制【链接】

代码实现


1.返回倒数第K个节点【链接】

题目描述:

实现一种算法,找出单向链表中倒数第 k 个节点。返回该节点的值。

思路快指针先走k步,然后快指针和慢指针同时走,直到快指针走到NULL,此时慢指针的节点即为所求。

解析: 

代码实现

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     struct ListNode *next;* };*/int kthToLast(struct ListNode* head, int k){struct ListNode*fast=head,*slow=head;  //快指针先走k步while(k--){fast=fast->next;}//快慢指针一起走while(fast){slow=slow->next;fast=fast->next;}return slow->val;
}

2.链表的回文结构【链接】

题目描述:

对于一个链表,请设计一个时间复杂度为O(n),额外空间复杂度为O(1)的算法,判断其是否为回文结构。

给定一个链表的头指针A,请返回一个bool值,代表其是否为回文结构。保证链表长度小于等于900。

思路首先找到中间节点,将中间节点后半部分倒置,再分别从头结点和尾节点向中间遍历,看对应值是否相等。

这里需要用到曾经写过的找链表的中间节点函数和反转链表函数可参照【单链表的应用】

代码实现 

/*
struct ListNode {int val;struct ListNode *next;ListNode(int x) : val(x), next(NULL) {}
};*/
class PalindromeList {public:/*** Definition for singly-linked list.* struct ListNode {*     int val;*     struct ListNode *next;* };*/struct ListNode* middleNode(struct ListNode* head) {//创建快慢指针struct ListNode* slow = head;struct ListNode* fast = head;//如果有两个中间节点,则返回第二个中间节点while (fast && fast->next) {slow = slow->next;fast = fast->next->next;}//此时slow刚好指向中间节点return slow;}struct ListNode* reverseList(struct ListNode* head) {// 重新创建一个链表,将之前的链表进行头插即可struct ListNode* rphead = nullptr;// 进行指针变换struct ListNode* cur = head;while (cur != nullptr) {// 用于保存下一个节点地址struct ListNode* newnode = cur->next;// 头插cur->next = rphead;rphead = cur;cur = newnode;}return rphead; //返回新链表的头rhead}bool chkPalindrome(ListNode* A) {struct ListNode* mid = middleNode(A);struct ListNode* rmid = reverseList(mid);while (rmid && A) {if (rmid->val != A->val) {return false;}rmid = rmid->next;A = A->next;}return true;}
};

3.相交链表【链接】

题目描述:

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。

示例1相交节点不是1,而是8,注意这里不能比值,因为两个不同节点的值可能一样,要比较节点的地址。

思路1:暴力求解,对于链表A中的每个节点,我们都遍历一次链表B看B中是否有相同节点,第一个找到的就是第一个公共节点,假设A链表有M个节点,B链表有N个节点,时间复杂度太高了为O(M*N)即O(N^2)。

思路2:先判断两个链表是否相交,可以通过判断两个链表最后一个节点地址是否相同,如果尾节点相同,说明两个链表一定相交,如果不相等,直接返回空指针。计算出两个链表的长度,让长链表先走相差的长度,然后让两个链表同时走,直到遇到相同的节点就是第一个公共节点,返回指向这个节点的指针。

解析: 

 

代码实现

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     struct ListNode *next;* };*/
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {struct ListNode *curA=headA,*curB=headB;int lenA=0,lenB=0;while(curA->next){curA=curA->next;      ++lenA; }while(curB->next){curB=curB->next;     ++lenB; //此时B链表的长度少算一个}//尾节点不相等就是不相交if(curA!=curB){return NULL;}lenA+=1;lenB+=1;//长的先走差距步,再同时走,第一个相等的就是交点//假设法int gap=abs(lenA-lenB);//求绝对值struct ListNode *longList=headA,*shortList=headB;//longList指向长链表,shprtList指向短链表//如果B比A长,再修改一下指针的指向,让longList指向长链表B,shprtList指向短链表Aif(lenB>lenA){longList=headB;shortList=headA;}while(gap--)//走差距步{longList=longList->next;}while(longList!=shortList){longList=longList->next;shortList=shortList->next;}return shortList;//返回哪一个都可以
}

4.判断链表中是否有环【链接】

题目描述:

给你一个链表的头节点 head ,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。

如果链表中存在环 ,则返回 true 。 否则,返回 false 。

思路首先,让快慢指针fast、slow指向链表的头节点head, 让快指针fast一次向后移动两个节点,慢指针一次向后移动一个节点, 判断fast和slow是否走到同一个节点上数学上追击相遇问题,如果走到同一个节点上,就返回true。 

代码实现

bool hasCycle(struct ListNode *head) {struct ListNode*slow=head,*fast=head;while(fast&&fast->next)//当一个链表中没有环时,fast一定会移动到链表的结尾{slow=slow->next;fast=fast->next->next;if(slow==fast){return true;}}return false;
}

常见问题解析 

1.快慢指针为什么会相遇,它们有没有可能错过,永远追不上?

 

不会错过。

假设slow进环时,fast 和 slow 的距离是N,追击过程中二者距离变化如下:

N->N-1->N-2->……->3->2->1->0

每追击一次,二者之间距离缩小1,距离为0即相遇。

2.慢指针slow一次移动一个节点,快指针一次移动多个节点(3,4,5……n)可行吗?

可行。

用快指针一次移动3个节点举例

假设slow进环时,fast 和 slow 的距离是N,追击过程中二者距离变化如下:

情况1:N->N-2->N-4->……->4->2->0(N为偶数)

情况2:N->N-2->N-4->……->5->3->1->-1(N为奇数)N为-1表示错过了,距离变成C-1(C为环的长度)

  • 如果C-1是偶数,新一轮追击就追上了
  • 如果C-1是奇数,那么就永远追不上

如果N是奇数并且C是偶数,那么就永远追不上,那这种条件存在吗?

假设slow进环前走的距离为L,设slow进环时,fast已经在环中转了x圈

slow走的距离:L

fast走的距离:L+x*C+C-N

由fast走的距离是slow的三倍产生的数学等式:3L=L+x*C+C-N

化简得:2L=(x+1)*C-N

我们发现偶数=(x+1)*偶数-奇数不成立,反证出当N是奇数时,C也一定是奇数

总结一下:一定能追上,N是偶数,第一轮就追上了;N是奇数,第一轮追不上,C-1是偶数,第二轮就追上了。

5.寻找环的入口点【链接】

题目描述:

给定一个链表的头节点  head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

不允许修改 链表。

思路1用两个指针head、meet分别指向链表的头节点和快慢指针相遇的节点,同时移动两个指针,当两个指针指向同一个节点时,该节点就是环的入口点 。

解析: 

假设环的长度为C,到达相遇点时,慢指针slow走过的距离为L+N(一圈之内肯定会被追上),快指针fast走过的距离为L+x*C+N (假设快指针走了x圈,N一定大于等于1),由fast走的距离是slow的2倍产生的数学等式:2*(L+N)=L+x*C+N  化简得:L=x*C-N

也就是说head到入口点的距离等于meet指针转x圈减去N的距离,head走到入口点,meet也刚好走到入口点,所以两个指针一起遍历,最终会同时到达入口点。

代码实现1

struct ListNode *detectCycle(struct ListNode *head) {struct ListNode *slow=head,*fast=head;while(fast&&fast->next){slow=slow->next;fast=fast->next->next;if(slow==fast){struct ListNode *meet=slow;while(meet!=head){meet=meet->next;head=head->next;}   return meet;}}return NULL;
}

思路2:让快慢指针相遇节点与下一个节点断开,然后将问题转化为两个链表的相交,环的入口点其实就是两个链表的相交点。

解析: 

代码实现2

 struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {struct ListNode *curA=headA,*curB=headB;int lenA=0,lenB=0;while(curA->next){curA=curA->next;      ++lenA; }while(curB->next){curB=curB->next;     ++lenB; //此时B链表的长度少算一个}//尾节点不相等就是不相交if(curA!=curB){return NULL;}lenA+=1;lenB+=1;//长的先走差距步,再同时走,第一个相等的就是交点//假设法int gap=abs(lenA-lenB);//求绝对值struct ListNode *longList=headA,*shortList=headB;//longList指向长链表,shprtList指向短链表//如果B比A长,再修改一下指针的指向,让longList指向长链表B,shprtList指向短链表Aif(lenB>lenA){longList=headB;shortList=headA;}while(gap--)//走差距步{longList=longList->next;}while(longList!=shortList){longList=longList->next;shortList=shortList->next;}return shortList;//返回哪一个都可以
}struct ListNode *detectCycle(struct ListNode *head) {struct ListNode *slow=head,*fast=head;while(fast&&fast->next){slow=slow->next;fast=fast->next->next;if(slow==fast){struct ListNode *meet=slow;struct ListNode *newhead=meet->next;meet->next=NULL;        return getIntersectionNode(head,newhead);}}return NULL;
}

6.随机链表的复制【链接】

题目描述:

给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,该指针可以指向链表中的任何节点或空节点。

构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成,其中每个新节点的值都设为其对应的原节点的值。新节点的 next 指针和 random 指针也都应指向复制链表中的新节点,并使原链表和复制链表中的这些指针能够表示相同的链表状态。复制链表中的指针都不应指向原链表中的节点 

深拷贝就是拷贝一个值和指针指向都跟当前链表一模一样的链表。 

思路: 拷贝节点到原节点的后面,再寻找途径处理random指针,处理完后对复制的节点拿下来尾插成新链表,返回新链表的头。

解析: 

代码实现

/*** Definition for a Node.* struct Node {*     int val;*     struct Node *next;*     struct Node *random;* };*///拷贝节点插入在原节点的后面
struct Node* copyRandomList(struct Node* head) {struct Node*cur=head;while(cur){struct Node*copy=(struct Node*)malloc(sizeof(struct Node));copy->val=cur->val;copy->next=cur->next;//注意这里顺序不能颠倒cur->next=copy;cur=copy->next;}//控制randomcur=head;while(cur){struct Node*copy=cur->next;if(cur->random==NULL){copy->random=NULL;}else {            copy->random=cur->random->next;}cur=copy->next;        }//把拷贝节点取下来尾插成为新链表,然后恢复原链表struct ListNode*copyhead=NULL,*copytail=NULL;cur=head;while(cur){struct ListNode*copy=cur->next;struct ListNode*next=copy->next;if(copytail==NULL){copyhead=copytail=copy;}else{copytail->next=copy;copytail=copytail->next;}cur->next=next;//恢复原链表,有没有这句代码都行!cur=next;}return copyhead;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/55860.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何进行数据中心负载测试的自动化?

数据中心负载测试的自动化是一种通过使用软件工具和脚本来模拟大量用户访问数据中心的过程,以评估其性能、稳定性和可扩展性的方法。以下是进行数据中心负载测试自动化的一些建议: 市场上有许多负载测试工具可供选择,如LoadRunner、JMeter、…

字节跳动青训营开始报名了!

关于青训营: 青训营是字节跳动技术团队发起的技术系列培训 &人才选拔项目;面向高校在校生,旨在培养优秀且具有职业竞争力的开发工程师。 本次技术训练营由掘金联合豆包MarsCode 团队主办课程包含前端、后端和 A 方向,在这个飞速发…

盲拍合约:让竞拍更公平与神秘的创新解决方案

目录 前言 一、盲拍合约是什么? 二、盲拍合约工作原理 1、合约创建与初始化 2、用户出价(Bid) 3、出价结束 4、披露出价(Reveal) 5、处理最高出价 6、结束拍卖 7、退款与提款 三、解析盲拍合约代码…

adum1201数字隔离器中文资料与应用

ADuM1201是ADI公司推出的一款数字隔离器,其典型应用有工业自动化、通讯电源管理、医疗设备以及汽车等领域。本文将对ADuM1201数字隔离器进行详细的介绍和应用分析,以帮助读者更好地了解和使用该产品。 一、ADuM1201数字隔离器概述 1、基本参数 ADuM120…

达梦DBLINK访问ORACLE配置方法

目录 1、概述 2、测试环境 3、语法简介 4、配置访问DM的DBLINK 5、配置访问ORACLE的DBLINK 5.1 通过OCI配置 5.2 通过ODBC配置 1、概述 本文介绍了达梦DBLINK的配置方法。有3部分内容,1)达梦访问到达梦的配置方法;2)通过OC…

LabVIEW程序怎么解决 Bug?

在LabVIEW开发过程中,发现和解决程序中的Bug是确保系统稳定运行的关键环节。由于LabVIEW采用图形化编程方式,Bug的排查和处理与传统编程语言略有不同。以下是解决LabVIEW程序中Bug的常见方法和技巧,涵盖从问题发现到解决的多个步骤和角度&…

php常用的注释符号

如果没有安装vscode和小皮,请点击下方链接安装: Vscode、小皮面板安装-CSDN博客 在学习php过程中,肯定少不了注释,也可以理解为备注的信息,来提醒自己这段代码有什么用,是什么意思等,接下来就介…

Android Studio Koala Feature Drop 稳定版现已推出

作者 / Android Studio 产品经理 Sandhya Mohan Android Studio Koala Feature Drop (2024.1.2) 现已推出!🐨 🔗 Android Studio https://developer.android.google.cn/studio 今年早些时候,我们宣布每个 Android Studio 动物版本…

秋天来临,猫咪又到换毛季,掉毛严重怎么办?宠物空气净化器有用吗?

秋天到了,新一轮的宠物换毛季又来了。谁能想到这只胖猫和之前刚接回来时的皮包骨小猫是同一只!除了养了一年长了些肉外,更多的都是换毛季掉毛”膨胀“的。每天下班回家都要搞卫生,家里衣服上、地板上,目光所及之处都有…

跟《经济学人》学英文:2024年10月05日这期 Workouts for the face are a growing business

Workouts for the face are a growing business They may not help much in the quest for eternal youth 原文: The FaceGym studio in central London looks more like a hair salon than a fitness studio. Customers recline on chairs while staff pummel t…

若依项目搭建(黑马经验)

欢迎你搜索和了解到若依,这个项目是从黑马课程的一个实践,更多的项目经历和平台搭建期待着我们的共同学习! 关于若依 若依是一套全部开源的快速开发平台,毫无保留给个人及企业免费使用。 前端采用Vue、Element UI。后端采用Sprin…

技术分享 —— JMeter接口与性能测试实战!

前言 在软件开发和运维过程中,接口性能测试是一项至关重要的工作。JMeter作为一款开源的Java应用,被广泛用于进行各种性能测试,包括接口性能测试。本文将详细介绍如何使用JMeter进行接口性能测试的过程和步骤。 JMeter是Apache组织开发的基…

JavaGuide(3)

一、项目背景与简介 JavaGuide由GitHub用户Snailclimb开发并维护,是一个全面而深入的Java学习资源库。它旨在为Java初学者和有经验的开发者提供一个系统的学习路径和丰富的资源,帮助他们系统地学习和巩固Java及相关技术知识。 二、项目内容与特点 Jav…

Unity实现自定义图集(四)

以下内容是根据Unity 2020.1.0f1版本进行编写的   在之前的篇章中已经把自定义图集在编辑器上的使用,以及运行时所需的信息都准备好了,接下来就是魔改UGUI的Image组件,使其能够像Image那样运行时如果引用的资源有打自定义图集,则加载对应自定义图集的Texture。 1、思路 …

IDM6.42下载器最新版本,提速你的网络生活!

🚀【速度与激情,IDM 6.42来袭!】💣 Hey, 亲爱的下载达人们!👋 今天我要给你们安利一个神器——Internet Download Manager(简称IDM),版本6.42,这可不是普通的…

DeepACO:用于组合优化的神经增强蚂蚁系统解决TSP问题的代码阅读

总体概括 DeepACO与普通ACO不同的是将问题输入实例输入到一个训练的网络中,将网络训练成为一个类似于专家知识的模块,可以生成相应的启发式矩阵网络,从而省去相应的专家知识。 其中在训练网络的代码中: 是进行监督式训练通过trai…

大模型基础:基本概念、Prompt、RAG、Agent及多模态

随着大模型的迅猛发展,LLM 作为人工智能的核心力量,正以前所未有的方式重塑着我们的生活、学习和工作。无论是智能语音助手、自动驾驶汽车,还是智能决策系统,大模型都是幕后英雄,让这些看似不可思议的事情变为可能。本…

java中的I/O(8个案例+代码+效果图)

目录 1.File类 1)常用构造方法 1)File(String pathname) 2)File(String parent, String child) 3)File(File parent, String child) 2)常用方法 1)boolean canRead() 2)boolean canWrite() 3&am…

计算机网络——ftp

在网络通信中,控制连接和数据连接是两种不同类型的连接,它们各自具有特定的功能和用途。 一、控制连接 定义与功能: 控制连接主要用于在通信双方之间传输控制信息,以建立、维护和终止数据连接。它负责协调和管理数据传输的过程&am…

Leetcode - 周赛418

目录 一,3309. 连接二进制表示可形成的最大数值 二,3310. 移除可疑的方法 三,3311. 构造符合图结构的二维矩阵 四,3312. 查询排序后的最大公约数 一,3309. 连接二进制表示可形成的最大数值 本题数据范围较小&#…