0. 写在前面的话
做题之后的整理与总结,和做题本身同样重要。
目前为止,LeetCode上标签为 “Linked List” 的题目共计35道,排在最后面的是第1019题。在这35道题目中,有5道题是需要解锁才可以做的。剩余的30题,我都做完了。
下面,我会把这30道题做一个详细的整理。
1. 基本介绍
链表是一种根据元素逻辑结果排列起来的数据结构,和数组比较像。但是这两者的区别在于,数组的长度是固定的,而链表的长度是不确定的。在现实情况中,需要保存的元素长度往往是不固定的,此时就可以使用链表进行存储。
在每个节点中,主要保存的属性是数据(data)和下一个节点的引用(next)。
在进行链表操作的时候,首先需要的是一个根节点(第一个节点即为根节点),然后每一个节点的引用都保存在上一节点的next属性中。而在进行输出的时候也应该按照节点的先后顺序,一个一个取得每一个节点所包含的数据。
2. 主要技巧
2.1 使用辅助头节点
当链表原来的头节点可能会被改变的时候,我们往往会使用一个伪节点放在链表的最前面,方便遍历链表、返回正确的结果。这个伪节点就是辅助头节点。
什么时候用辅助头节点,什么时候不用呢?
当头结点可能被反转、删除、改变时,或者头节点不存在(链表为空)时,就需要使用辅助头节点,便于返回正确的结果。
对应题目:
19. Remove Nth Node From End of List
删除链表中倒数第 n 个元素。原来的头节点可能被删除掉,所以需要使用辅助头节点。
203. Remove Linked List Elements
删除链表中所有元素值为 val 的节点。原来的头节点可能被删除掉,所以需要使用辅助头节点。
2.2 反转一个单向链表
反转一个链表有很多方法,比较推荐的方法是改变节点的 next 指针。将每个节点的 next 指针修改为指向前一个元素。这样就完成了一个链表的反转。
对应题目:
206. Reverse Linked List
反转一个单向链表,建议熟练掌握。反转链表是解决一些题目的重要步骤,如果这一步骤都不会,就无法做出来整个题目了。
2.3 双指针法
双指针法是一种非常常见的方法,强烈建议熟练掌握。这个方法也很灵活,可以做很多事情。在链表中可以用来确定固定的长度的位置、寻找链表的中点、将链表一分为二等等。
对应题目:
19. Remove Nth Node From End of List
删除链表中倒数第 n 个元素。借助双指针“平移窗口”,来确定倒数第n个元素的位置。
876. Middle of the Linked List
找出链表的中点,这个方法是非常有用的,很多题目都涉及找出中点的操作。
3. 题目总结
以下就是全部链表类的题目总结了,题目的排列顺序是从简单到困难。
如果读者是刚刚接触编程的新手,我建议每题不必深究,无论用什么方法,只要成功过题即可。
但是如果读者已经掌握了至少一种编程语言,那么我强烈建议仔细分析每个题的解法,分析不同解法的时空复杂度,选取最优的解法解题。
从简单至复杂的顺序排列
203. Remove Linked List Elements
删除链表中所有元素值为 val 的节点。通过此题要熟悉设置辅助节点的技巧、删除某个节点的方法。
19. Remove Nth Node From End of List
删除链表中倒数第 n 个元素。从这个题中,可以学习遍历一个链表的方法、设置辅助空节点的技巧。同时也可以通过此题学会使用双指针法。
21. Merge Two Sorted Lists
合并两个有序链表,要求合并后的新链表也是有序的。此题也适合入门练习。
23. Merge k Sorted Lists
合并 k 个有序链表到一个链表中,合并后的新链表也必须是有序的。是 21. Merge Two Sorted Lists 的加强版本。很多方法都可以解决此题,个人推荐分治法。
876. Middle of the Linked List
找出链表的中点,这个题需要用到双指针法。
83. Remove Duplicates from Sorted List
给出一个有序链表,删除链表中的重复元素。在成功完成本题后,不妨想一下如果链表没有经过排序,如何删除其中的重复元素?
82. Remove Duplicates from Sorted List II
和83题非常相似,同样是有序链表,但是要求删除所有出现次数 1 次以上的元素。只保留出现次数刚好 1 次的元素。可以利用此题练习双指针法。
24. Swap Nodes in Pairs
对链表中的节点进行交换,这也是基本操作之一。
237. Delete Node in a Linked List
删除单向链表中的一个节点。这个题的独特之处在于,不给出链表,只给出要删除的点。它使用了一个很巧秒的方法解决,可以拓宽思路。
86. Partition List
分割链表,将一个链表分割成比 x 小的部分和比 x 大的部分。通过这道题,可以练习遍历链表、为链表增加节点、拼接两个链表等基本操作。
61. Rotate List
给出一个链表,向右循环链表 k 位,k是一个非负整数,求新的链表。这个题是 189. Rotate Array 旋转数组的链表版本。
206. Reverse Linked List
反转一个单向链表,建议熟练掌握。反转链表是解决一些题目的重要步骤,如果这一步骤都不会,就无法做出来整个题目了。
92. Reverse Linked List II
反转一个单项链表的一小段,是 206. Reverse Linked List 的拓展延伸
25. Reverse Nodes in k-Group
这个题还是反转链表,是每 k 个节点就反转一下。是 206. Reverse Linked List 和 92. Reverse Linked List II 两题的拓展延伸。
138. Copy List with Random Pointer
给定一个链表,这个链表的每个节点都包含了一个额外的随机指针。返回一个链表的深拷贝。这个题需要理解深拷贝的概念,了解链表在java中的存储机制。
707. Design Linked List
设计一个链表类。这个题不难,只涉及对链表的简单操作。但是代码的行数会比较多。而且有一个神坑。
430. Flatten a Multilevel Doubly Linked List
遍历一个有子链表的双向链表。尽管此题被贴上了链表的标签,但是题目中主要考察的是深度优先搜索,链表只是一种外在的形式。
817. Linked List Components
给出一个链表和一个数组G。数组G中的元素是链表所有节点 val 值的子集。如果某个节点或者某一段节点的 val 值在数组G中,那么就被看作一个“组件”。求最终求整个链表一共有多少个“组件”?这个题需要用到集合,可以借此熟悉集合的用法。
2. Add Two Numbers
给出两个非空的链表,代表着2个非负整数。求这2个非负整数的和,也使用链表表示出来。
445. Add Two Numbers II
本题和 2. Add Two Numbers 非常像,唯一不同的地方在于链表表示整数是从高位到低位表示的。计算的过程中需要使用堆栈。可以借此熟悉堆栈的用法。
1019. Next Greater Node In Linked List
给出一个链表,求每个节点后面第一个val值更大的节点。为了降低时间复杂度,这个题使用了堆栈、还将链表的遍历顺序改变了。有点像动态规划的思路了,也就是利用之前计算存储下来的结果,计算当前的问题。
725. Split Linked List in Parts
把一个链表分割为 k 个尽量相等的部分。这个题目练习了如何从一个大链表中截取一小部分的链表。
328. Odd Even Linked List
把链表的奇数节点都排在偶数节点的前面,使用双指针法。不过本题的双指针法严格意义上来说,是用了三个指针的。
147. Insertion Sort List
使用插入排序对一个链表进行排序。需要学会如何在一个有序链表中插入元素,并且保证插入元素后链表还是有序的。
141. Linked List Cycle
给定一个链表,判断该链表中是否有环。该题可以用hash集合做,也可以用双指针法做。这两种方法都很常见,建议把它们都熟练掌握。
160. Intersection of Two Linked Lists
写一个程序去寻找两个单向链表相交的节点。本题同样有两种方法,一种是集合,一种是双指针法。可以当作双指针法的题目练习。
142. Linked List Cycle II
和141. Linked List Cycle非常类似,同样是给出一个链表,判断该链表中是否有环。如果有环,就返回环形开始的那个节点。如果这个链表中没有环,那么就返回null。方法仍然是集合、双指针法。
109. Convert Sorted List to Binary Search Tree
给出一个按照递增排序的单向链表,将其转换成一个高度平衡的二叉树。这个题目也利用了快慢双指针法找到链表的中点。该方法在 141. Linked List Cycle 和 142. Linked List Cycle II中都有使用过。
234. Palindrome Linked List
判断一个链表是不是回文串,主要用到了从一个链表中间一分为二的方法,以及反转链表的方法。
148. Sort List
对链表进行归并排序。需要掌握归并排序的方法、将链表从中间一分为二的方法和合并两个有序链表的方法。
143. Reorder List
这个题目是一道综合题,涉及三种针对链表的操作:
1 . 如何从中间将链表一分为二?使用了 109. Convert Sorted List to Binary Search Tree 中介绍的快慢双指针法
2. 如何反转一个链表?使用了 206. Reverse Linked List 中反转链表的方法,即节点的next指向前一个节点
3. 如何拼接两个链表?使用了类似于 21. Merge Two Sorted Lists 和 2. Add Two Numbers 中的合并链表的方法