6
6
7
7
- null/nil 异常处理,多写点None的分支没事的;
8
8
- dummy node 哑节点:避免处理head导致的问题,反正最后返回就是返回dummy.next;
9
- - 快慢指针,有时也会有三个指针的情况,比如反转链表。
9
+ - 快慢指针,比如找中点。 有时也会有三个指针的情况,比如反转链表。
10
10
- 插入一个节点到排序链表
11
11
- 从一个链表中移除一个节点
12
12
- 翻转链表:不好搞了就创一个新的链表从头开始,只操作头节点。链表问题搞不清楚都可以这么思考;
@@ -296,44 +296,46 @@ class Solution:
296
296
> 将其重新排列后变为: * L* →* L\_\_ n* →* L* →* L\_\_ n* →* L* →* L\_\_ n* →…
297
297
298
298
- 思路:找到中点断开,翻转后面部分,然后合并前后两个链表
299
+ - 注意点:快慢指针,翻转链表,合并链表,前面几道题目的组合
300
+ - 拿到题目时不要慌,纸上推一下,就会发现动的是后面一半的节点,不要被题目示意图迷惑。
299
301
300
302
``` Python
301
303
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 :
313
305
"""
314
306
Do not return anything, modify head in-place instead.
315
307
"""
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 :
317
309
return
318
310
319
311
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:
322
313
slow = slow.next
323
-
324
- h, m = head, slow.next
314
+ fast = fast.next.next
315
+ mid = slow.next
325
316
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
337
339
```
338
340
339
341
### [ linked-list-cycle] ( https://leetcode-cn.com/problems/linked-list-cycle/ )
@@ -342,7 +344,7 @@ class Solution:
342
344
343
345
- 思路1:Hash Table 记录所有结点判断重复,空间复杂度 O(n) 非最优,时间复杂度 O(n) 但必然需要 n 次循环
344
346
- 思路2:快慢指针,快慢指针相同则有环,证明:如果有环每走一步快慢指针距离会减 1,空间复杂度 O(1) 最优,时间复杂度 O(n) 但循环次数小于等于 n
345
- ![ fast_slow_linked_list ] ( https://img.fuiboom.com/img/fast_slow_linked_list.png )
347
+ - 另一种理解方法:快慢指针进入环中,必然会相遇,相遇就是有环。
346
348
347
349
``` Python
348
350
class Solution :
@@ -352,7 +354,7 @@ class Solution:
352
354
353
355
while fast is not None and fast.next is not None :
354
356
slow = slow.next
355
- fast = fast.next.next
357
+ fast = fast.next.next
356
358
if fast == slow:
357
359
return True
358
360
@@ -364,6 +366,7 @@ class Solution:
364
366
> 给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 ` null ` 。
365
367
366
368
- 思路:快慢指针,快慢相遇之后,慢指针回到头,快慢指针步调一致一起移动,相遇点即为入环点。
369
+ - 为什么呢?因为快指针每次移动 2 步,慢指针每次移动 1 步,所以快指针走过的路程是慢指针的 2 倍,所以快指针走过的路程为慢指针的 2 倍时,快慢指针相遇。
367
370
368
371
![ cycled_linked_list] ( https://img.fuiboom.com/img/cycled_linked_list.png )
369
372
@@ -387,13 +390,14 @@ class Solution:
387
390
return None
388
391
```
389
392
390
- 坑点
393
+ 坑点:
391
394
392
- - 指针比较时直接比较对象,不要用值比较,链表中有可能存在重复值情况
393
- - 第一次相交后,快指针需要从下一个节点开始和头指针一起匀速移动
395
+ 1 . 指针比较时直接比较对象,不要用值比较,链表中有可能存在重复值情况
396
+ 2 . 第一次相交后,快指针需要从下一个节点开始和头指针一起匀速移动
394
397
398
+ ** 注意** :* 找中点和找环的快慢指针起始不一样*
395
399
396
- 注意, 此题中使用 slow = fast = head 是为了保证最后找环起始点时移动步数相同,但是作为找中点使用时** 一般用 fast=head.Next 较多** ,因为这样可以知道中点的上一个节点,可以用来删除等操作。
400
+ 此题中使用 slow = fast = head 是为了保证最后找环起始点时移动步数相同,但是作为找中点使用时** 一般用 fast=head.Next 较多** ,因为这样可以知道中点的上一个节点,可以用来删除等操作。
397
401
398
402
- fast 如果初始化为 head.Next 则中点在 slow.Next
399
403
- fast 初始化为 head,则中点在 slow
@@ -402,7 +406,17 @@ class Solution:
402
406
403
407
> 请判断一个链表是否为回文链表。
404
408
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)
406
420
407
421
``` Python
408
422
class Solution :
@@ -432,66 +446,68 @@ class Solution:
432
446
> 要求返回这个链表的 深拷贝。
433
447
434
448
- 思路1:hash table 存储 random 指针的连接关系
449
+ - 对于python这种解释性语言,可以在原始节点对象上,直接利用属性存储random链接信息
435
450
436
451
``` Python
437
452
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
463
474
```
464
475
465
476
- 思路2:复制结点跟在原结点后面,间接维护连接关系,优化空间复杂度,建立好新 list 的 random 链接后分离
477
+ - 三次遍历:复制节点、建立 random 链接、分离新 list
478
+ - 注意:复制节点时维护好原来的 next 指针,避免混乱
466
479
467
480
``` Python
468
481
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 :
472
485
return None
473
486
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
495
511
```
496
512
497
513
## 总结
0 commit comments