反转链表
核心:想清楚反转的每一步迭代状态、目的,以及相对应的要点。
状态:当前节点的上一节点已反转,也就是pre指向了pre的上一节点,pre和curr之间没有连接,curr和curr.next正向连接。
目的:
- 反转curr,指向pre,curr和curr.next无连接。
- 继续迭代,即更新pre和curr。
要点:
- 循环终止条件:由于每一步需要反转curr,而curr为空时无法反转,所以终止循环。
- 先反转,再迭代,反转后没有节点指向curr.next,因此反转前需要注意保存节点curr。
- 涉及和上一节点的交互,需要处理链表边界条件:dummy node。
反转链表II
在【反转链表】的基础上,关键是区分left、right、中间节点的不同反转方式。
如何只遍历一次?记录left及前序节点,right及后序节点,遍历完成后重新连接边界即可。
细节:
- 一般都需要dummy node。
- 需要修改节点交互关系的,一定注意保存临时节点。
K个一组翻转链表
在【反转链表II】的基础上,再加一层。整体思路简单,最外层循环遍历节点,每k个调用一次【反转链表II】,如果不满k个直接返回头节点。
细节:
- 返回头节点时,永远返回dummy.next,尤其是在这种修改节点关系的题目中,修改后原head已经不再是head了。
- 每次在k个节点内部完成反转,需要1)修改外部节点和start、end的连接关系。2)更新pre和curr便于下一次迭代,注意pre更新为新的end而非curr。
移除链表
基础题,做完【反转链表】再做这个,从整体思路是来说易如反掌。
但是,需要关注细节:
- pre节点如何更新,取决于是否删除节点。
- 一定要有dummy,最终返回结果一定要是dummy.next,以应对删除head的多种情况(后续是否有节点等)。
相交链表
第一时间应该想到哈希表:
- 本质上是说哪个节点同时出现在了两个链表里。
- 但这种解法没有利用链表结构。
更优解法:
- 双链表,用双指针遍历。
- 找出相交节点:如何让两个指针同时遍历到相交节点?链表相连。
合并两个有序链表
迭代思路:
- 双链表,用双指针遍历。因为需要新链表,所以再加一个指针。
- 新链表:dummy node起手,最终返回dummy.next即可。
递归思路:
- 可以迭代求解时,都多考虑一步递归。未见得可行,未见得更优,纯思维训练。
- 定义清楚状态即可,如果 l1 或者 l2 一开始就是空链表 ,那么没有任何操作需要合并,所以我们只需要返回非空链表。否则,我们要判断 l1 和 l2 哪一个链表的头节点的值更小,然后递归地决定下一个添加到结果里的节点。如果两个链表有一个为空,递归结束。
分隔链表
核心:这道题和【K个一组翻转链表】类似,算法本身简单,关键是如何写出思路清晰、没有bug的代码。
思路:
- 如何分隔?很简单,分成k组,n//k作为每组的基数,前n%k组的节点个数需要+1。
- 如何实现?
- 遍历一次链表,先计算出几个数字。
- 二次遍历,产出列表。二次遍历,如何定义状态?
- 直接的思路,是一层遍历,带着当前的group和group内部的数量,依次遍历每个节点。
- 也可以压缩状态,两层遍历,状态更简洁。外层对group遍历,内层对group内部数量遍历。对复杂度没有影响。
- 细节:
- 由于返回的是节点的列表,即“分隔的”链表“片段”,每次产出一个片段,需要将尾节点的下一节点置空。
- 凡是涉及置空,就需要引入tmp,储存节点。
环形链表
和【相交链表】类似,本质上是说有没有重复路过一个节点,因此首先考虑哈希表。
优化思路:同样参考【相交链表】,需要双指针。由于是同一链表,通过快慢指针构造双指针。
细节:
- 快慢指针初始位置相同,while循环内先更新指针,再比较是否相等。
- while循环条件:
- 快指针在前,循环条件以快指针为准,即快指针不为空。
- 快指针一次走两步,为了避免循环体内加判断,直接在while循环判定fast.next不为空。
环形链表II
在【环形链表】基础上,从定性(是否成环)变味了定量(返回入环节点)。
思路一,仍然采用哈希表,简单,返回第一个已拜访节点即可。
思路二,数学解,和【相交链表】思路类似,分段定义距离。
- 环前a,入环相遇处b,环内剩余部分c。慢指针走了a+m*(b+c)+b,快指针走了a+n*(b+c)+b。
- 同时,快指针速度是慢指针的两倍,即a+(n+1)b+nc = 2a+(2m+2)b+2mc,得到a=(n-2m-1)*(b+c) + c。
- 环前部分,刚好等于从相遇处 出发,走X圈,回到入口的距离。
- 因此,加入新指针,在快慢指针相遇时,与慢指针同步速出发。新指针与慢指针相遇位置,即为入环节点。
随机链表的复制
因为引入random指针,一次遍历无法解决问题。
- 一次遍历,哈希表记录原节点-新节点映射关系。
- 二次遍历,新节点赋值。