Skip to content

Commit eb2ae7c

Browse files
链表
1 parent 6060d0a commit eb2ae7c

File tree

1 file changed

+100
-84
lines changed

1 file changed

+100
-84
lines changed

2.data_structure/2.linked_list.md

Lines changed: 100 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
- null/nil 异常处理,多写点None的分支没事的;
88
- dummy node 哑节点:避免处理head导致的问题,反正最后返回就是返回dummy.next;
9-
- 快慢指针,有时也会有三个指针的情况,比如反转链表。
9+
- 快慢指针,比如找中点。有时也会有三个指针的情况,比如反转链表。
1010
- 插入一个节点到排序链表
1111
- 从一个链表中移除一个节点
1212
- 翻转链表:不好搞了就创一个新的链表从头开始,只操作头节点。链表问题搞不清楚都可以这么思考;
@@ -296,44 +296,46 @@ class Solution:
296296
> 将其重新排列后变为: *L**L\_\_n**L**L\_\_n**L**L\_\_n*→…
297297
298298
- 思路:找到中点断开,翻转后面部分,然后合并前后两个链表
299+
- 注意点:快慢指针,翻转链表,合并链表,前面几道题目的组合
300+
- 拿到题目时不要慌,纸上推一下,就会发现动的是后面一半的节点,不要被题目示意图迷惑。
299301

300302
```Python
301303
class Solution:
302-
303-
def reverseList(self, head: ListNode) -> ListNode:
304-
305-
prev, curr = None, head
306-
307-
while curr is not None:
308-
curr.next, prev, curr = prev, curr, curr.next
309-
310-
return prev
311-
312-
def reorderList(self, head: ListNode) -> None:
304+
def reorderList(self, head: Optional[ListNode]) -> None:
313305
"""
314306
Do not return anything, modify head in-place instead.
315307
"""
316-
if head is None or head.next is None or head.next.next is None:
308+
if head is None or head.next is None:
317309
return
318310

319311
slow, fast = head, head.next
320-
while fast is not None and fast.next is not None:
321-
fast = fast.next.next
312+
while fast and fast.next:
322313
slow = slow.next
323-
324-
h, m = head, slow.next
314+
fast = fast.next.next
315+
mid = slow.next
325316
slow.next = None
326-
327-
m = self.reverseList(m)
328-
329-
while h is not None and m is not None:
330-
p = m.next
331-
m.next = h.next
332-
h.next = m
333-
h = h.next.next
334-
m = p
335-
336-
return
317+
318+
dummy = ListNode()
319+
curr = mid
320+
while curr:
321+
curr_next = curr.next
322+
curr.next = None
323+
old_dummy_head = dummy.next
324+
new_dummy_head = curr
325+
dummy.next = new_dummy_head
326+
new_dummy_head.next = old_dummy_head
327+
curr = curr_next
328+
329+
curr = dummy.next
330+
pointer = head
331+
while curr:
332+
curr_next = curr.next
333+
curr.next = None
334+
pointer_next = pointer.next
335+
pointer.next = curr
336+
curr.next = pointer_next
337+
curr = curr_next
338+
pointer = pointer_next
337339
```
338340

339341
### [linked-list-cycle](https://leetcode-cn.com/problems/linked-list-cycle/)
@@ -342,7 +344,7 @@ class Solution:
342344
343345
- 思路1:Hash Table 记录所有结点判断重复,空间复杂度 O(n) 非最优,时间复杂度 O(n) 但必然需要 n 次循环
344346
- 思路2:快慢指针,快慢指针相同则有环,证明:如果有环每走一步快慢指针距离会减 1,空间复杂度 O(1) 最优,时间复杂度 O(n) 但循环次数小于等于 n
345-
![fast_slow_linked_list](https://img.fuiboom.com/img/fast_slow_linked_list.png)
347+
- 另一种理解方法:快慢指针进入环中,必然会相遇,相遇就是有环。
346348

347349
```Python
348350
class Solution:
@@ -352,7 +354,7 @@ class Solution:
352354

353355
while fast is not None and fast.next is not None:
354356
slow = slow.next
355-
fast = fast.next.next
357+
fast = fast.next.next
356358
if fast == slow:
357359
return True
358360

@@ -364,6 +366,7 @@ class Solution:
364366
> 给定一个链表,返回链表开始入环的第一个节点。  如果链表无环,则返回  `null`
365367
366368
- 思路:快慢指针,快慢相遇之后,慢指针回到头,快慢指针步调一致一起移动,相遇点即为入环点。
369+
- 为什么呢?因为快指针每次移动 2 步,慢指针每次移动 1 步,所以快指针走过的路程是慢指针的 2 倍,所以快指针走过的路程为慢指针的 2 倍时,快慢指针相遇。
367370

368371
![cycled_linked_list](https://img.fuiboom.com/img/cycled_linked_list.png)
369372

@@ -387,13 +390,14 @@ class Solution:
387390
return None
388391
```
389392

390-
坑点
393+
坑点
391394

392-
- 指针比较时直接比较对象,不要用值比较,链表中有可能存在重复值情况
393-
- 第一次相交后,快指针需要从下一个节点开始和头指针一起匀速移动
395+
1. 指针比较时直接比较对象,不要用值比较,链表中有可能存在重复值情况
396+
2. 第一次相交后,快指针需要从下一个节点开始和头指针一起匀速移动
394397

398+
**注意***找中点和找环的快慢指针起始不一样*
395399

396-
注意,此题中使用 slow = fast = head 是为了保证最后找环起始点时移动步数相同,但是作为找中点使用时**一般用 fast=head.Next 较多**,因为这样可以知道中点的上一个节点,可以用来删除等操作。
400+
此题中使用 slow = fast = head 是为了保证最后找环起始点时移动步数相同,但是作为找中点使用时**一般用 fast=head.Next 较多**,因为这样可以知道中点的上一个节点,可以用来删除等操作。
397401

398402
- fast 如果初始化为 head.Next 则中点在 slow.Next
399403
- fast 初始化为 head,则中点在 slow
@@ -402,7 +406,17 @@ class Solution:
402406

403407
> 请判断一个链表是否为回文链表。
404408
405-
- 思路:O(1) 空间复杂度的解法需要破坏原链表(找中点 -> 反转后半个list -> 判断回文),在实际应用中往往还需要复原(后半个list再反转一次后拼接),操作比较复杂,这里给出更工程化的做法
409+
- 思路:O(1) 空间复杂度的解法需要破坏原链表
410+
- 找中点 -> 反转后半个list -> 判断回文
411+
- 在实际应用中往往还需要复原(后半个list再反转一次后拼接)
412+
- 操作比较复杂
413+
- 这里给出更工程化的做法:
414+
- 利用到栈结构
415+
- 找中点时在栈中记录节点值
416+
- 然后遍历后半部分链表,借用栈的先进后出刚好反着判断
417+
- 注意 fast is not None 情况,这表明链表长度为奇数,slow 指针需要再走一步
418+
- 反正是注意检查链表的奇偶情况
419+
- 这种方法虽然不会破坏链表,但空间复杂度为 O(n/2)
406420

407421
```Python
408422
class Solution:
@@ -432,66 +446,68 @@ class Solution:
432446
> 要求返回这个链表的 深拷贝。
433447
434448
- 思路1:hash table 存储 random 指针的连接关系
449+
- 对于python这种解释性语言,可以在原始节点对象上,直接利用属性存储random链接信息
435450

436451
```Python
437452
class Solution:
438-
def copyRandomList(self, head: 'Node') -> 'Node':
439-
440-
if head is None:
441-
return None
442-
443-
parent = collections.defaultdict(list)
444-
445-
out = Node(0)
446-
o, n = head, out
447-
while o is not None:
448-
n.next = Node(o.val)
449-
n = n.next
450-
if o.random is not None:
451-
parent[o.random].append(n)
452-
o = o.next
453-
454-
o, n = head, out.next
455-
while o is not None:
456-
if o in parent:
457-
for p in parent[o]:
458-
p.random = n
459-
o = o.next
460-
n = n.next
461-
462-
return out.next
453+
def copyRandomList(self, head: 'Optional[Node]') -> 'Optional[Node]':
454+
dummy = Node(x=-1)
455+
old_curr, new_curr = head, dummy
456+
while old_curr:
457+
new_node = Node(x=old_curr.val)
458+
new_curr.next = new_node
459+
old_curr.new = new_node
460+
old_curr = old_curr.next
461+
new_curr = new_curr.next
462+
463+
old_curr, new_curr = head, dummy.next
464+
while old_curr:
465+
old_random = old_curr.random
466+
if old_random:
467+
new_curr.random = old_random.new
468+
else:
469+
new_curr.random = None
470+
old_curr = old_curr.next
471+
new_curr = new_curr.next
472+
473+
return dummy.next
463474
```
464475

465476
- 思路2:复制结点跟在原结点后面,间接维护连接关系,优化空间复杂度,建立好新 list 的 random 链接后分离
477+
- 三次遍历:复制节点、建立 random 链接、分离新 list
478+
- 注意:复制节点时维护好原来的 next 指针,避免混乱
466479

467480
```Python
468481
class Solution:
469-
def copyRandomList(self, head: 'Node') -> 'Node':
470-
471-
if head is None:
482+
def copyRandomList(self, head: 'Optional[Node]') -> 'Optional[Node]':
483+
484+
if not head:
472485
return None
473486

474-
p = head
475-
while p is not None:
476-
p.next = Node(p.val, p.next)
477-
p = p.next.next
478-
479-
p = head
480-
while p is not None:
481-
if p.random is not None:
482-
p.next.random = p.random.next
483-
p = p.next.next
484-
485-
new = head.next
486-
o, n = head, new
487-
while n.next is not None:
488-
o.next = n.next
489-
n.next = n.next.next
490-
o = o.next
491-
n = n.next
492-
o.next = None
493-
494-
return new
487+
old_curr = head
488+
while old_curr:
489+
old_next = old_curr.next
490+
old_curr.next = Node(
491+
x=old_curr.val,
492+
next=old_curr.next,
493+
)
494+
old_curr = old_next
495+
496+
old_curr = head
497+
while old_curr:
498+
new_curr = old_curr.next
499+
if old_curr.random:
500+
new_curr.random = old_curr.random.next
501+
old_curr = new_curr.next
502+
503+
new_head = new_curr = head.next
504+
while new_curr:
505+
old_curr = new_curr.next
506+
if old_curr:
507+
new_curr.next = old_curr.next
508+
new_curr = new_curr.next
509+
510+
return new_head
495511
```
496512

497513
## 总结

0 commit comments

Comments
 (0)