目次
Δ前言
一、两个升序链表归并为一个降序链表
0.题目:
1.算法设计思想:
2.C语言形貌:
3.算法的时间和空间复杂度:
二、两个链表的所有相同值结点天生一个新链表
0.题目:
1.算法设计思想:
2.C语言形貌:
3.算法的时间和空间复杂度:
三、两个链表的所有相同值结点放入到其中一个原链表中
0.题目:
1.算法设计思想:
2.C语言形貌:
3.算法的时间和空间复杂度:
四、判定一个链表是否为另一个链表的连续子序列
0.题目:
1.算法设计思想:
2.C语言形貌:
3.算法的时间和空间复杂度:
五、判定一个循环双链表是否对称
0.题目:
1.算法设计思想:
2.C语言形貌:
3.算法的时间和空间复杂度:
六、两个循环单链表的连接 (链接)
0.题目:
1.算法设计思想:
2.C语言形貌:
3.算法的时间和空间复杂度:
七、循环单链表反复找到最小值结点并删除
0.题目:
1.算法设计思想:
2.C语言形貌:
3.算法的时间和空间复杂度:
八、非循环双链表查改结点并按照结点访问频度举行排序
0.题目:
1.算法设计思想:
2.C语言形貌:
3.算法的时间和空间复杂度:
九、查找单链表中倒数第K个结点 [2009真题]
0.题目:
1.算法设计思想:
2.C语言形貌:
3.算法的时间和空间复杂度:
十、两个链表存储两个单词求共同后缀的起始位置 [2012真题]
0.题目:
1.算法设计思想:
2.C语言形貌:
3.算法的时间和空间复杂度:
十一、链表绝对值的去重 [2015真题]
0.题目:
1.算法设计思想:
2.C语言形貌:
3.算法的时间和空间复杂度:
十二、链表元素的重新交织分列 [2019真题]
0.题目:
1.算法设计思想:
2.C语言形貌:
3.算法的时间和空间复杂度:
Δ总结
Δ前言
- 从408应试的角度来看,算法题主要分为四部分——①线性表(包罗数组和链表);②二叉树;③图;④查找。本篇博文主要讲链表相关的底子算法(由于链表相关的算法题目较多,所以up分为了上下两篇博文)。
- 博文中涉及到的题目全部参考自《王道数据结构考研复习指导》和《竟成408考研复习全书》。
- 注意事项——①点击文章侧边栏目次大概文章开头的目次可以举行跳转。②所有题目都符合408算法题的题目格式,即第一问算法设计思想、第二问C语言形貌该算法、第三问盘算该算法的时空复杂度。
- 良工不示人以朴,up所有文章都会适时补充完满。各人假如有问题都可以在批评区举行交换大概私信up。感谢阅读!
一、两个升序链表归并为一个降序链表
0.题目:
已知有两个升序的单链表(按照结点的值递增排序),现要求将这两个升序的单链表归并为一个降序的单链表,而且要求除了原本两个升序链表的结点外,在归并过程中不允许创建新的结点。
请设计一个算法来实现上述操作,用C语言形貌该算法,而且盘算出该算法的时间复杂度和空间复杂度。
1.算法设计思想:
我们早在“C 408—《数据结构》算法题底子篇—数组”一文中就打仗过归并思想。它的算法思想是:重新到尾依次比较两个次序表中的元素,并把更小的谁人元素放在最终的次序表中,直到合并完毕;只不外如今从"次序表"换成"链表"了,在次序中我们是通过三个下标实现的,那么在链表中我们自然要通过三个指针来实现了。
p1指针指向listA链表的结点,p2指针指向listB链表的结点。利用while循环遍历两个升序链表,每次将更小的谁人结点头插到新的链表中。(题干要求我们从“升序”变为“降序”,不难想到要用到头插法,因为头插法可以逆置链表)
2.C语言形貌:
完整代码如下:(注意看解释)
运行结果 :
3.算法的时间和空间复杂度:
(1) 时间复杂度 :
O(m + n). 这是合并两个链表的最佳时间复杂度.
(2) 空间复杂度 :
O(1). 因为我们没有创建新的结点(题目也不允许我们创建), 整个过程只利用了几个临时的辅助变量, 所以该算法是"原地工作"的.
二、两个链表的所有相同值结点天生一个新链表
0.题目:
设listA和listB是两个带有头结点的单链表,且listA和listB都是升序链表(结点的值递增有序),现要求在不破坏原有链表结点的前提下,产生一个新链表listC,listC中保存的结点是listA和listB的相同值的结点(注意是值相同)。
请设计一个算法来实现上述操作,用C语言形貌该算法,而且盘算出该算法的时间复杂度和空间复杂度。
1.算法设计思想:
题干中要求“不破坏原有链表结点”,阐明我们在遍历过程中需要创建新的结点。
利用while循环遍历listA和listB链表,每次循环都要比较当前listA和listB结点的值的大小(预设p,q指针分别指向listA和listB当前的结点),若值不相等,令值更小的指针后移;若值相等,就创建一个与当前值相等的结点,并将该结点插入到listC链表中,随后同时移动p,q指针后移,当p,q指针某一方为NULL时,阐明对应的链表已经遍历完毕,此时结束while循环,返回listC链表。
2.C语言形貌:
完整代码如下:(注意看解释)
运行结果 :
3.算法的时间和空间复杂度:
(1) 时间复杂度 :
The time complexity is O(n + m), where n and m are the lengths of listA and listB respectively. This is optimal for this problem.
(2) 空间复杂度:
The space complexity is O(min{n, m}) in the worst case, which is also optimal.
三、两个链表的所有相同值结点放入到其中一个原链表中
0.题目:
已知两个升序的单链表listA和listB,要求将两个链表的所有公共值结点全部放入到单链表listA中。
请设计一个算法来实现上述操作,用C语言形貌该算法,而且盘算出该算法的时间复杂度和空间复杂度。
1.算法设计思想:
此题要与上一题类比举行理解,上一题不允许我们破坏原有结点,所以我们需要创建新的结点来天生最终的链表,这就导致算法的空间复杂度更高了,达不到算法”原地工作“的结果。而此题中,要求我们用原来的链表listA维护所有的公共值结点,所以肯定涉及到了链表的删除,就需要我们在上一题的底子上做一些小小的改动。
仍然利用while循环遍历listA和listB链表,对于listA链表,利用两个指针currentA和prevA分别指向listA链表的当前结点和前一个结点。每次循环都要比较当前listA和listB结点的值的大小,若值不相等,令值更小的指针后移,假如值更小的结点是listA链表中的结点,就删除该结点,并令指向listA结点的两个指针都后移一位,假如值更小的结点是listB链表中的结点,就仅移动指向listB链表中的结点,不删除结点;若值相等,就同时移动指向listA和listB链表的指针,假如listA链表比listB链表长,那么当listB链表遍历完毕时,listA链表还没有遍历完毕,但可以肯定剩下的元素都不是公共值元素,所以还需要补一个while循环用来删除listA表中剩余的结点。
2.C语言形貌:
完整代码如下:(注意看解释)
运行结果 :
3.算法的时间和空间复杂度:
(1) 时间复杂度 :
The time complexity is O(n + m), where n and m are the lengths of listA and listB respectively. This is optimal for this problem.
(2) 空间复杂度:
The space complexity is O(1) as it operates in-place on listA.
四、判定一个链表是否为另一个链表的连续子序列
0.题目:
现有两个带有头结点的单链表listA和listB,要求判定listB是否为listA链表的一个连续子序列。
请设计一个算法来实现上述操作,用C语言形貌该算法,而且盘算出该算法的时间复杂度和空间复杂度。
1.算法设计思想:
- “暴力法”,分别用currentA和currentB两个指针指向listA和listB的当前结点,另设指针prevA记载每一轮循环的currentA是从哪个结点开始遍历的,
- listA和listB均重新开始遍历,判定当前对应的结点是否相等(指值相等),
- 对于第一次判定,假如不相等,令currentA指针后移一位(此时currentB指针仍然指向listB链表的首结点),重新开始判定;假如相等,就令currentA和currentB指针同时向右移,并再次判定新指向的对应的结点是否相等,
- 假如再次判定的结果不相等,阐明之前判定相等的结点到这儿就卡住了,形不成同listB一样的连续序列,也就是说此次循环中,cuttentA从prevA记载的位置开始遍历listA是找不到和B一样的连续序列的(注意此时currentB指针已经发生了移动),
- 那么,出现失败的情况后,对于listA,令currentA指向prevA记载结点的下一个结点,并令currentB的指针重新回到listB的首结点,开始新的一轮循环,
- 假如发现任意一方的指针指向NULL时,跳出循环,此时判定currentB是否为NULL,只要currentB为NULL,阐明肯定在listA中找到了一个与listB相同的连续子序列。
2.C语言形貌:
完整代码如下:(注意看解释)
运行结果 :
3.算法的时间和空间复杂度:
(1) 时间复杂度 :
In the worst case scenario:
You might need to check every node in listA as a potential starting point for a match.
For each starting point, you might need to compare with all nodes in listB.
So the time complexity is O(n * m) , where n is the number of nodes in listA and m is the number of nodes in listB.
(2) 空间复杂度:
Uses only a fixed number of additional pointers (currentA, lastTimeA, currentB) regardless of the input size.
It doesn't use any additional data structures that grow with the input size.
Therefore, the space complexity is O(1) - constant space.
五、判定一个循环双链表是否对称
0.题目:
有一个带有头结点的循环双链表,判定其是否为对称的链表(讨论对称不包罗头结点)。
请设计一个算法来实现上述操作,用C语言形貌该算法,而且盘算出该算法的时间复杂度和空间复杂度。
1.算法设计思想:
注意,此题与之前所有的题目都不同——此题的操尴尬刁难象是双链表,而且是循环双链表,这意味着我们需要去重新界说新的数据结构,以及新的用于链表初始化的方法,和用于遍历链表,开释链表内存的方法。
关于链表是否对称,其实就是从链表的两端开始,每一对对应的结点的值都相同,不难想到会出现链表个数为奇数和链表个数为偶数这两种情况(一样平常讨论链表结点数时,不考虑头结点,因为头结点并不承载有效数据),利用两个指针left 和 right分别初始化为链表的尾结点和首结点,left指针以尾结点到首结点方向从右向左遍历,right指针则从首结点开始,依次向右遍历,只要left和right指向的结点的数据域相同,就继续遍历下去。
奇数个结点和偶数个结点的差异,体如今遍历什么时候结束。对于奇数个结点的循环双链表,假如left和right即将指向的下一个结点是同一个结点,阐明之前遍历过的对应结点都是data域相同的,到这里就该结束了;对于偶数个结点的循环双链表,假如left指向的下一个结点是right当前指向的结点,大概假如right指向的下一个结点是left当前指向的结点,阐明遍历到这里也该结束了,这是一个对称的链表。反之,只要在遍历过程中发现有任何一对结点的值不对应相等,阐明这不是一个对称的链表,直接return就可以了。
2.C语言形貌:
完整代码如下:(注意看解释)
运行结果 :
3.算法的时间和空间复杂度:
(1) 时间复杂度 :
The time complexity is O(n/2), which simplifies to O(n), where n is the number of nodes in the list.
(2) 空间复杂度:
Therefore, the space complexity is O(1) (constant space).
六、两个循环单链表的连接 (链接)
0.题目:
给出两个均带有头结点的循环单链表,链表头指针分别为pHeadA and pHeadB,请设法将链表pHeadB链接到链表pHeadA的后面,要求得到的新链表仍然保持循环的特性。
请设计一个算法来实现上述操作,用C语言形貌该算法,而且盘算出该算法的时间复杂度和空间复杂度。
1.算法设计思想:
这个没啥可讲的吧,就和链表结点的尾插类似,只不外如今是要插一个链表,把指针看清晰别瞎几把指就行。
对了,这个题和上一个题又不一样,这个题的操尴尬刁难象是循环单链表,这意味着我们得在之前单链表界说好的数据结构和相关函数的底子上,做一些改动,比方说遍历链表的函数,以及开释链表内存的函数。
2.C语言形貌:
完整代码如下:(注意看解释)
运行结果 :
3.算法的时间和空间复杂度:
(1) 时间复杂度 :
The total time complexity is O(n + m).
(2) 空间复杂度:
Space Complexity of insertList: O(1).
七、循环单链表反复找到最小值结点并删除
0.题目:
已知一个带有头结点的循环单链表,要求不断地找出链表中的最小值结点,把最小值结点的值打印出来而且删除该结点,直到该链表一个有效结点不剩,最后再删除头结点。
请设计一个算法来实现上述操作,用C语言形貌该算法,而且盘算出该算法的时间复杂度和空间复杂度。
1.算法设计思想:
在“C—408《数据结构》算法题底子篇—链表(上)”一文中,也就是链表专题的上篇,有一道题目是删除链表的最小值结点,其时up已经较为具体地先容了删除最小值结点需要的两个操作“找”和“删”,这里就不再赘述,假如有对思绪大概相关代码有不懂的朋友们可以点击链接再去回顾一下(就在第二道题);
而此题相当于之前那道题的升级版,主要差异在了两个地方——①本题操作的链表是循环链表;②本题并不是只删除一个最小值结点,而是要逐个击破,赶尽杀绝(bushi,所以up重点说一下如何处置惩罚这两个问题:针对第一个差异,只需要注意循环链表的判空条件与非循环链表不同即可;针对第二个差异,只需要在之前那道题的底子上再套一层循环即可。
2.C语言形貌:
完整代码如下:(注意看解释)
运行结果 :
3.算法的时间和空间复杂度:
(1) 时间复杂度 :
- The outer while loop runs until the list is empty, so it executes n times, where n is the initial number of nodes in the list.
- For each iteration of the outer loop, the inner while loop traverses the entire remaining list to find the minimum value.
- In the first iteration, it checks n nodes, in the second n-1, then n-2, and so on.
- This forms an arithmetic sequence: n + (n-1) + (n-2) + ... + 2 + 1
- The sum of this sequence is n(n+1)/2
- Therefore, the time complexity is O(n^2)
(2) 空间复杂度:
- The algorithm uses a fixed number of pointers (tempPre, minPre, temp, min) regardless of the input size.
- No additional data structures are created that grow with the input size.
- The space used is constant throughout the execution of the algorithm
- Overall Space Complexity: O(1)
八、非循环双链表查改结点并按照结点访问频度举行排序
0.题目:
已知一个带有头结点的非循环双向链表,该链表的每个结点都维护有四个属性——①pev(指向当前结点的前驱结点);②data(当前结点的有效值);③freq(当前结点的访问频度,初始值为0);④next(指向当前结点的后继结点)。现界说一个Locate(pHead, data)操作,该操作会访问链表中元素值为data的结点,并将该结点的freq域的值加一,同时,还会令当前链表结点保持freq域递减的次序举行分列,而且,当两个结点的freq域的值相同时,要求最近被访问的结点优先排在前面,以便使得频繁访问的结点总是更靠近链表头。要求界说一个函数实现上述Locate(pHead, data)操作,而且返回目的结点的指针。(假设给定的链表已经按照freq降序排序)
请设计一个算法来实现上述操作,用C语言形貌该算法,而且盘算出该算法的时间复杂度和空间复杂度。
1.算法设计思想:
先对题干举行分解,可知该算法共需要完成五个步骤—①查找链表中指定值的结点;②修改该结点的值;③取出该结点(涉及到双链表中结点的删除);④查找到第一个freq域比该结点大的结点;⑤将该结点插入到第一个freq域比该结点大的结点的后面(涉及到双链表中结点的插入)。
这五个操作都很底子,因此这是一道综合题,但算不上难题,每个操作本身都没难度,注意双链表结点的删除和插入时,指针给指对就行,别瞎 |