链表

题目汇总

1、移除元素 

203. 移除链表元素

237. 删除链表中的节点(链表IQ题)

83. 删除排序链表中的重复元素(递归、迭代)

82. 删除排序链表中的重复元素 II(递归+讨论、迭代)

19. 删除链表的倒数第 N 个结点(双指针)

876. 链表的中间结点(快慢指针)

2、设计链表(集中基本操作,建立链表、插入、删除)

707. 设计链表

3、反转链表

206. 反转链表(递归)

92. 反转链表 II

234. 回文链表(部分反转)

25. K 个一组翻转链表(反转区域头插法)

24. 两两交换链表中的节点(快慢指针、迭代)

4、链表相交

面试题 02.07. 链表相交

142. 环形链表 II

5、合并链表

21. 合并两个有序链表

23. 合并K个升序链表

6、综合

143. 重排链表

86. 分隔链表(模拟)

303. 区域和检索 - 数组不可变


1、203. 移除链表元素

1)问题描述

移除链表元素

2)思路

(1)通过递归的方法(先进后出),从链表最后一个元素遍历到表头

递归

代码实现--递归

✨复杂度分析:时间复杂度,空间复杂度。

(2)迭代(实现过程力扣)

迭代

迭代-不释放多余结点
含有多余结点释放操作

✨时间复杂度为,空间复杂度为。

tips:

(i)如果只是传递地址,则不需要申请动态空间,因为不需要建立结点。

(ii)题目这里的头结点数据域非空,是链表的开始。

同类练习:

83. 删除排序链表中的重复元素

82. 删除排序链表中的重复元素 II


2、707. 设计链表

1)问题描述

设计链表(注意带index的示例)

2)代码实现(单链表)力扣

定义结构体
建表
查找链表元素
从头部添加元素
从尾部添加结点
从指定位置添加元素
删除指定位置元素
删除链表

(tips:尝试写个双链表、循环链表)



3、206. 反转链表

1)问题描述

反转链表-示例1
反转链表-示例2

2)思路

(1)迭代方法,但是注意用一个结点存储前一个结点的信息,方便后续赋值,而每次存储使用完了记得更新!!!(就是因为没有更新,所以当时在写的时候出现了死循环)

迭代法

时间复杂度为,其中为链表长度,需要遍历链表一次。

空间复杂度为.

(2)递归

递归

时间复杂度为,其中 n是链表的长度。需要对链表的每个节点进行反转操作。

空间复杂度为,其中 n是链表的长度。空间复杂度主要取决于递归调用的栈空间,最多为 n 层。

同类练习:

92. 反转链表 II

234. 回文链表

25. K 个一组翻转链表


4、24. 两两交换链表中的节点

1)问题描述

两两交换链表元素

2)思路分析

通过观察我发现如果把整张链表分奇数项和偶数项,实际上是发生了“移位”,于是我想到,用两个指针,一个指向当前结点的p指针,一个是指向当前结点的前驱指针prev,通过改变指向达到目的。

这里有几个细节需要注意:

(1)更改链表的操作的工具:指针 + 虚拟头指针(这需要额外申请空间)固定格式:

建立虚拟指针

(2)需要考虑几个特殊情况:i) 空链表;ii)奇数个元素的链表。

(3)链表头两个结点发生对换之后,头结点发生了改变,这时候要告诉程序“头结点”是谁(对head重新赋值)

(4)程序返回结果是head 还是dummyhead -> next?

3)代码实现

两两对换链表元素-代码实现

对比题解:

3.1)递归法 力扣

递归

时间复杂度是,其中 n是链表的节点数量。需要对每个节点进行更新指针的操作。

空间复杂度是,其中 n是链表的节点数量。空间复杂度主要取决于递归调用的栈空间。

3.2)迭代法 力扣

迭代法

时间复杂度为,其中 n 是链表的节点数量。需要对每个节点进行更新指针的操作。

空间复杂度为


5、19. 删除链表的倒数第 N 个结点

比较常规简单,关键是感受一下栈方法、双指针法

1)栈方法

栈方法-先进后出

时间复杂度为,其中是链表的长度。

空间复杂度为,为链表长度,主要为栈的开销。

2)双指针法

双指针法间接定位目标位置

时间复杂度为。

空间复杂度为。

⚠️这里面思想很精妙的地方在于:我要找倒数第n个元素,就相当于找顺数的l - n个(l为链表长度)。

利用这个思路,我让first指针走n步。

然后让first和second一起走(second是从头部开始的,first从第n个位置),这时候,当first走到链表尾部的时候,second和first一共走了 l - n步。也就是second定位到了倒数第n个元素。

精妙的地方在于它选用了参考系first来定位。

3)递归方法

递归效果

6、面试题 02.07. 链表相交

这一题要做出来不难,但这里面有好思路值得学习。

1)问题描述

问题描述 - 示例1
问题描述-示例2

2)分析

这一道题有好多方法值得借鉴学习。

1)最常规想法:把两个链表先“预处理”到同一起跑线上(起点到终点位置长度一样),然后再遍历比较。力扣,这里有一个技巧,就是规定curA永远指向较长的链表!!!(遗留问题:如何写一个交换函数,能够交换不同数据类型的元素??)

2)建立两个栈分别存储两个链表,根据栈前进后出(FILO)特点,比较。力扣,注意,虽然说栈存储了链表元素,但是,比较的是栈元素的“内部成员”,不是栈元素(结构体)本身。

节选

3)通过观察,两个指针(curA,curB)分别从两个链表出发(headA,headB),当遍历完各自的链表,就跳转到对方的链表(curA = headB,curB = headA),遍历到最后都有curA == curB,只是若有交点,则curA == curB && curB != NULL,如果没有交点,则curA == curB == NULL 。

观察

7、142. 环形链表 II

理解(参考力扣)

环形链表找环入口

同类型:

1)141. 环形链表


8、21. 合并两个有序链表

1)问题描述

合并链表

2)代码赏析:

(1)精简,几行代码描述多种情况。

上面示例涵盖三种情况


只用两行代码就囊括了三种情形
迭代法



9、23. 合并K个升序链表

1)问题描述

合并k个升序链表问题描述

2)思路

(1)由上一题,不停调用两两合并的链表。力扣

复杂度分析

(2)利用分治方法,链表两两一个组,先进行合并,合并后的链表由再两两一组,进行合并,知道最后合并为一个链表。力扣

不停两两合并

复杂度分析

分治方法



10、876. 链表的中间结点

1)问题描述

中间结点

2)分析

这道题要实现不难,有一个很棒的技巧——快慢指针法(前面环形链表的技巧),当fast走到最后时,slow一定走到了链表中间了,这时候只要返回slow即可。这里面有一个值得注意的地方,就是用快慢指针的时候,while循环体的判断语句,用(fast != NULL && fast -> next != NULL),否则,若fast -> next == NULL,那么循环体内的fast更新(fast = fast -> next -> next;)会出现报错。

快慢指针法

你可能感兴趣的:(链表)