前言
最近把leetcode的linked list专题刷完了,我感觉收获很多,也看了很多他人的代码,对单链表的理解又更深了一步,所以写篇博文来总结一下,同时也方便自己以后复习。
leetcode上的链表节点定义如下:
|
|
设置哨兵节点
哨兵(sentinel)是个哑元节点(dummy node),可以简化边界条件,使代码更紧凑,但对速度并没有什么帮助。
单链表反转
使用三个指针完成单链表的反转。
|
|
单链表并归排序(merge sort)
采用递归的方法,Linked List中的很多问题都可以采用递归的方法。比较直接。
伪代码:
|
|
其中 merge(a, b);
是合并两个有序链表。
1/2 链表的位置:可以采用两个指针,一个一步/次,一个两步/次,从而得到链表1/2的位置。
代码:
|
|
判断链表是否有环并找到环入口
判断是否有环:
slow
指针一次走一步:slow = slow->next;
fast
指针一次走两步: fast = fast->next->next;
若两者相遇: fast == slow 则存在环。
若(fast->next == NULL || fast == NULL) 则不存在环。
找环的入口:
我们定义变量如下图:
在相遇点(meet):
slow
指针走了: \(s = l + a \)fast
指针走了: \(2s = l + a + n * r\)
所以 \(l + a = n r = (n - 1) r + r\)
又因为 \( a + x = r\)
所以 \(l = (n - 1) * r + x\)
即从begin
到entrance
的距离等于从meet
点到entrance
的距离。
所以,当两者相遇后,一个指针回到起点,另一个不动,两者每次都走一步,等两者相遇,相遇点
就是环的入口(entrance)。
单链表的很多问题采用递归很容易求解
leetcode: Reverse Nodes in K-Group
伪代码:
|
|
这里再给出一种非递归
的写法:
|
|
Merge K个有序单链表
方法一:
两两先合并,再一步步合成一个。(类似并归的思想)
注意:
List 1与List 2合并,List3与List4合并,最后再把他们的结果合并,这样做一般比按顺序合并要效率高。
方法二:
利用堆来合并。
建立一个大小为K的堆v(最大堆还是最小堆看情况而定)。make_heap(v.begin(), v.end());
以最小堆为例:
每次选出最小的节点,将其加入新链表的尾部,再将该节点的next节点加入堆中。以此类推直到堆为空。
注意:
v是vector容器,v.pop()后,v再 v.push_back(min->next),再push_heap(v.begin(), v.end()),成为新的最小堆。
插入排序
建立哨兵节点是操作方便ListNode dummy(INT_MIN);
可以做剪枝:cur
指针不用每次返回表头,例如对于升序排序的情况,仅当cur->val
大于插入节点的val
,cur
才返回表头。
代码如下:
|
|
复制带一个随机指针的单链表
leetcode: copy list with random pointer
这题挺有意思,要复制一个带随机指针的单链表,关键就是如何解决这个随机指针的复制。
核心思路:
在原来的链表节点之间插入新的节点即可。
这样随机节点可被巧妙复制!
例如:new2->random = old2->random->next
最后新节点两两相连即可!
代码如下:
|
|