forked from cookieisaac/leetcode
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathLeetCode 做题笔记
5059 lines (3620 loc) · 176 KB
/
LeetCode 做题笔记
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
LeetCode 做题笔记
前面的想起来再记录吧!
5. Longest Palindromic Substring
隔了半年了。终于把马拉车算法给实现了。
算法关键:
1. 预处理string。 把"ABC"处理成"^#A#B#C#$"就可以了。
2. P[i]表示从下标i开始,可以向单边延伸的长度。
并且,P[i]也是删除掉所有'#'符号之后的回文长度。
3. 计算原回文的位置。 start = (i - P[i]) // 2
4. 注意事项:
P[i] = min(P[i_mirror], R - i)
if i + P[i] > R:
center, R = i, i + P[i]
expand from center也是一个很不错的办法,而且挺好实现的。
13. Roman To Integer
testcase不足:考虑“IXCM”
14. Longest Common Prefix
divide and conqure方法!
step = 2
while(step < 2 * len(strs)):
for i in range(0, len(strs) - int(step / 2), step):
strs[i] = common_prefix(strs[i], strs[i + int(step / 2)])
step = step * 2
return strs[0]
要记住这个写法!
15. 3 Sums
经典题目还是要挤一挤!
关键点是sort,这样之后可以two pointer搜索。
16. 3Sum Closest
还是需要先sort,然后two pointer就可以了。
加上一些沙雕优化hhh
17. Letter Combinations of a Phone Number
直接建立一个mapping,然后backtracking也行。直接bfs生成也行。
21. Merge Two Sorted Lists
加一个Dummy就好写多了。
23. Merge K sorted List
每个list一个pointer,和merge 2 list类似做法?
那么找最小的需要 K 比较。 整体就是nK。
用min heap找最小, O(log(K)N)
Python3: from queue import PriorityQueue
Avoid compare between user-defined bojects, put( (value, idx, object) )
另一个思路, divide and conquer, 两两合并,不断汇总
25. Reverse Nodes in k-Group
emmm。
用递归做挺好做的。
不过用loop也可以做,二层反转。虽然代码不好写。
26. Remove Duplicates from Sorted Array
做吧。
29. Divide Two Integer
不能用乘除法和取余。
一直做减法,记count。 这是一种做法。
优化一下:1 2 4 8 16 做减法, 然后循环
30. Substring with Concatenation of All Words
好题。思路需要清晰。
有一个关键点是单词长度一样,所以可以遍历,要不然只能DFS。
不需要每一个下标都重置count和seen_word。
可以用类似KMP的思路,牢牢结合word长度一样这个特性,采用滑动窗口的思路做
退出码为0,成功找到一个candidate,i只能往前走一个word的长度
退出码为1,substring内单词个数超了,i往前走到合适位置
退出码为2,substring内有非法字符,i可以跳过全部
31. Next Larger Permutation
关键一:如果一个数是非升序排列,不可能有比它大的排列。
关键二:一个非升序排列,反转后就是升序排列,也就是最小的排列。
所以找最右边的升序pattern,从右往左找第一个比它大的,交换。
这是右边的仍然是非升序排列,反转后生成最小排列。
32. Longest Valid Parentheses
很关键的一点,我想了很久是错的。
不能用二分法找最大合法长度。
因为(())()(()),246都可以,但是8不行,但是10又可以。
不过验证是否存在长度为N的合法括号群,确实可以用O(N)实现。
用deque加滑动窗口实现嗷。
没做出来。看答案了。
都是O(n)就可以找到最长。
dp[i] = 2 + dp[i - 2] # case1: ....()
dp[i] = 2 + dp[i - 1] + dp[i - dp[i - 1] - 2] # case2: ....))
用stack做也可以。
ans, stack = 0, [-1]
for i, c in enumerate(s):
if c == '(':
stack.append(i)
elif stack:
stack.pop()
if not stack:
stack.append(i)
else:
ans = max(ans, i - stack[-1])
还可以左边扫描一遍,右边扫描一遍。
33. Search in Rotated Sorted Array
这种题目,真的是我的软肋。
思路还是二分法,找到mid之后判断mid在高位还是低位。
35. Search Insert Position
就是binary search。需要复习。
37. Sudoku Solver
这应该是我接触的backtracking第一题。
我的做法是预先遍历一次grid,用三个collections.defaultdict(set)来做。
38. Count and Say
emmmm。。。考点是loop吗?
39. Combination Sum
典型的backtracking问题。
如何避免重复?加一个起始位置记录。
40. Combination Sum II
和 I 的区别是candidate不是set了。为了避免重复,需要sort。
并且step into的条件需要增加。
41. First Missing Positive
交换元素,放到该放的位置。记得用while。
至多发生N次交换,最坏再for loop访问N-1个元素。O(2n)。
或者不交换,用负号标记也可以。
42. Trapping Rain Water
横着想:
可以用stack。里面装(index,height)
对每个元素,如果height > top's height,就pop top。
temp = stack.pop(-1)
if len(stack) == 0: break
distance = i - stack[-1][0] - 1 //关键,长度是算的左边的bound
temp_height = min(stack[-1][1], h) - temp[1]
water += distance * temp_height
竖着想:
每一个slot可以放的水量是他左右max的min。 -> 直接搜索是O(N^2)
使用DP思路,从左到右,从右到左,记录最值,最后每个slot取两个值的较小值。
更进一步,2 pointers。左右各一个。
如果左边的height低于右边的height,相当于这个点的水量一定取决于left_max。
同理,如果右边的height低于左边,这个点水量一定取决于right_max。
43. Multiply Strings
和两个list相乘没啥区别。注意一下连续进位。(有办法可以不处理)
44. Wildcard Matching
和Facebook的正则匹配万分类似。
DP即可。wildcard要么不要,要么白嫖一位。
补充!DP太慢了!!!
这个题目有一个核心关键点:那就是如果出现了多个星号,我们只用记录最后一个星号的信息,因为前面的部分都是匹配成功的。
所以可以用 【双指针 + 回溯】 做。
如果出现了星号,就记录双指针的位置,下次匹配不成功就回溯,记得更新source指针 (increment by 1)
45. Jump Game II
最少步数。 bfs思路。类似sliding window。
⭐:从左往右找第一个能到终点的点,更新终点,直到终点为0。类似反向bfs思路。
46. Permutations
典型的backtracking题目。交换后recursion,之后交换回来。
另一种做法:反向一步一步生成。ans和new ans交换。
47. Permutation II
我的思路:数一下每个数有多少个,然后backtracking做。
另一种做法:反向生成。如何避免重复是关键!
如果在插入位置的元素和被插入元素一样,则break。
49. Group Anagrams
就是计算哈希值嘛。可以统计char的频率
50. Pow(x, n)
二分法思路
2^7 = 2 * 4^3 = 2 * 4 * 8 ^ 1
不同语言可能需要handle int_min。
51. N-Queens
第二道backtracking吧?
不知道queen的移动方式真的是太尴尬了。hhhhh
52. N-Queens II
和上面一道题是一样的。
可以用bitmap来做,因为不用保存结果。
53. Maximum Subarray
Kadane's algorithm.
额外考虑一下全负数的情况。
只需要把cur = max(0, cur + n)改成cur = max(0, cur) + n就好了。
55. Jump Game
DP例题。一开始的想法是从右往左记录每个点是否能到终点。
速度过慢。观察发现其实只要能够到最左边的good slot,就可以了。
最后优化为O(N)。
56. Merge Intervals
有很多写法。核心就是排序了。
我觉得比较简洁的写法是,修改result的末尾是一个好思路。
intervals.sort(key=lambda x: x.start)
result = []
for i in intervals:
if not result or result[-1].end < i.start:
result.append(i)
elif result[-1].end < i.end:
result[-1].end = i.end
return result
当然了,用一个pre来做也挺好的。
57. Insert Interval
嗯?这道题为啥是Hard?
模拟就可以了呀,O(n)。
59. Spiral Matrix II
必须学这个写法!!! 太震撼了
matrix = [[0] * n for _ in range(n)]
i = j = 0
di, dj = 0, 1
for k in range(1, n * n + 1):
matrix[i][j] = k
# 因为是从外走到内,所以外围一定是不是0,所以可以直接取模!!!
# 换方向也是牛逼!
if matrix[(i + di) % n][(j + dj) % n]:
di, dj = dj, -di
i += di
j += dj
return matrix
60. Permutation Sequence
因为只要找到特定的一个,所以不可能生成全部。
O(N^2)就可以生成。因为remove(index)是O(N)。
index = (k - 1) // mul
ans += candidates.pop(index)
k -= index * mul
61. Rotate List
可以直接跑一遍统计长度,求出来前面要放多少个。
把尾巴连接到头,再跑一遍找到新的尾巴,断开就可以了。
也可以把整个链表翻转,从后外前找新尾巴,断开后再翻转回来。
64. Minimum Path Sum
dijkstra用牛刀了。
1D DP就可以了。 因为只能向下向右走。
65. Valid Number
最最傻逼的题目!!!!
没有之一。
67. Add Binary
实现Full adder。没啥意思。
68. Text Justification
emmmm。。就是滑动窗口做嘛。。
比较难写就是了。
69. Sqrt(x) Integer.
这个题目大有文章可做。需要详细学习。
等差级数公式:
1 + 3 + 5 + ... + (2n - 1) = n ^ 2
所以简单的for loop就可以做。效率不行。
二分法:
x^2 <= n and (x+1)^2 > n, get n。
长除法:
https://zh.wikipedia.org/wiki/%E5%B9%B3%E6%96%B9%E6%A0%B9#.E7.89.9B.E9.A0.93.E6.B3.95
太牛了。
牛顿迭代法:
理解:利用斜率,选择起始点,不断逼近。
需要二阶可导,起始点选择有影响。
https://www.zhihu.com/question/20690553
选择起始点 X_n,切线方程为 y - f(X_n) = f'(X_n) * (x - X_n)
下一个迭代点: 设y = 0,解方程 f'(X_n) * (x - X_n) + f(X_n) = 0
⭐:X_n+1 = X_n - f(X_n) / f'(X_n)
运用到这道题目里,相当于求解 x^2 - n = 0。
这个函数二阶全程可导。 -> f'(X_n) = 2 * X_n
X_n+1 = X_n - f(X_n) / f'(X_n)
= X_n - (X_n ^ 2 - n) / (X_n * 2)
= 0.5 * (X_n + n / X_n)
设置起始点X_n = n,这样我们迭代时一直在根点的右侧。(因为n > 0)
当 X_n ^ 2 <= n停止。(这里也很神奇)
位运算: 还需要研究。
32位的整数,开方后最大为16位。我们只需要确定这16个bit是0还是1。
MSB是sign bit所以不管。
unsigned bitwise_verification(unsigned long x){
unsigned long temp = 0;
unsigned v_bit = 15;
unsigned n = 0;
unsigned b = 0x8000;
if (x <= 1) return x;
do {
temp = ((n << 1) + b) << (v_bit--);
if (x >= temp) {
n += b;
x -= temp;
}
} while (b >>= 1);
return n;
}
快速方法,不适用于大数: 0x5f3759df 0x5F375A86
float FastSqrt(float x) {
float n = x;
int i = *(int*)&x;
i = 0x1fbd1df5 + (i >> 1); // (1 - p)L(B - y) + p * x
x = *(float*)&i;
x = (x + n / x) * 0.5; // 收敛一次
return x;
}
e = E - B m = M / L
(1 + m) * (2 ^ e) M + E * L
71. Simplify Path
傻逼题目。
扫描过去就是if编程。
edge case是 "..filename" 和 "..."。
在结尾加一个'/'比较方便。
72. Edit Distance
非常有学习价值的一道题目!edit distance需要详细学习!
https://web.stanford.edu/class/cs124/lec/med.pdf
思路是DP。
DP[i][j]描述word1[0: i + 1]到word2[0 : j + 1]的edit distance。
转移方程是:
DP[i][j] = min (
DP[i - 1][j] + cost(deletion),
DP[i][j - 1] + cost(insertion),
DP[i - 1][j - 1] + cost(replace) if word1[i] != word2[j]
)
73. Set Matrix Zeroes
有一个很聪明的解法。
我们利用第一行和第一列表示当前行列是否需要被清空
因为要用第一列表示当前行是否被清空,所以我们需要特殊处理
74. Search a 2D Matrix
就是二分法,把一维的index转成二维的就好了。
75. Sort Colors
自己的想法是维护三个指针,代表012的对应位置。
不过这样就不是O(N)了。
维护两个就够了。只要保证0都在左边,2都在右边。1自己就对了。
还有一个方法也挺有意思的,维护三个指针
zero = one = two = -1
for i in range(len(nums)):
if nums[i] == 0:
zero, one, two = zero + 1, one + 1, two + 1
nums[two] = 2
nums[one] = 1
nums[zero] = 0
elif nums[i] == 1:
one, two = one + 1, two + 1
nums[two] = 2
nums[one] = 1
else:
two += 1
nums[two] = 2
76. Minimum Window Substring
滑动窗口标准题目。
missing记录剩余需要的char个数,needed记录每个需要char的个数。
77. Combinations
backtracking哈哈哈哈哈哈!
80. Remove Duplicates from Sorted Array II
和LC.26一样的做法。
用left和count两个变量就可以解决。
81. Search in Rotated Sorted Array II
LC. 33的follow up。
数组中可能有重复值,这样带来的结果就是最坏情况是O(n)。
不同于LC.33直接求mid,我们可以先判断lo和hi是否为一个值。
如果是的话,就increment lo,保证我们可以拿到高低两个区间。
82. Remove Duplicates from Sorted List II
不能修改值的话,就加一个dummy在前面。
和下面一道题一样的while loop,不过因为只保存独有的值,所以需要修改一些。
值得记录!
while cur:
if cur.next and cur.next.val == cur.val:
while cur and cur.next and cur.val == cur.next.val:
cur = cur.next
pre.next = cur.next #cur此时指向重复值的最后一个
else:
pre = pre.next #核心!
cur = cur.next
return dummy.next
83. Remove Duplicates from Sorted List
做呗。
如果不能改变值的话!
加一个dummy在最前面,扫描到每个重复的最后,连接。
84. Largest Rectangle in Histogram
我的做法是左右用单调栈各扫描一遍
思路是每个可能的长方形一定是这个高度乘上左右连续大于等于它的长度。
不过因为用了两次for loop,挺慢的
看solution发现stack方法可以优化到一次!
和我思路差不多,不过因为单调栈如果发现了一个比栈顶小的
那么其实当前栈顶元素的左右长度其实都被确定了。
不需要算一次左边,算一次右边。
85. Maximal Rectangle
想太复杂了。。。
可以直接转化成上面一道题的解法。。
还有一个解法,之后再学吧。。
86. Partition List
正常做,建立两个临时node优化代码。最后一定要把尾巴设置为None。
87. Scramble String
我真的没有想到。。居然就是和对称树差不多的做法!
小优化:如果长度小于4,必定可以,试试就可以
89. Gray Code
我的思路是模拟,O(2^n * n),因为要使用一个set
还有一个厉害的思路是观察出来的。
考虑一下n = 3
(000, 001, 011, 010), (110, 111, 101, 100)
每一轮都是前面剩下的镜像+前置位1
90. Subsets II
这个要考虑重复值,可以用一个count_dict来做。
也可以一开始sort一下,然后遍历的时候判断一下起始生成位置。(依据是否和前面元素相同)
91. Decode Ways
backtracking会超时。
其实是典型的dp问题,脑子不好,唉。
可以进一步优化为二变量。
92. Reverse Linked List II
recursion的比较好写。递归到m=1的情况再处理。
iterative的就比较难写一点,加一个dummy在前面,然后记录范围的左右。
93. Restore IP Addresses
啊,就是一道backtracking加上memo。
写的真的烦!
二刷了仍然很烦!
95. Unique Binary Search Trees II
这道题挺恶心的,不过代码部分感觉有意思。
我的傻逼办法是升序插入顺序,然后再按照顺序插入来生成。
不过仔细优化下,可以直接用一个生成方法递归就好了。
这个方法返回当前树的root,然后我们合并就好了。
非常类似merge sort。是一道锻炼代码实现的好题目。
96. Unique Binary Search Trees
一道典型的DP或者memorization题目。
dp[i] = sum(dp[j] * dp[i - j - 1], 0 <= j < i)
97. Interleaving String
backtracking with memorization就可以了。
还想省空间就DP吧,二维可以优化为一维。
DP[i][j]代表 interleave(s1.substring(0, i), s2.substring(0, j), s3.substring(0, i + j - 1))
转移方程的话 同行就看左边的,第一列看上面的,剩下的看左边或者上面。
DP[i][j] = (DP[i - 1][j] and s1[i] == s3[i + j + 1]) or (DP[i][j - 1] and s2[j] == s3[i + j + 1])
99. Recover Binary Search Tree
是一道有意思的题目。
利用的核心知识点其实是BST的中序遍历的输出,是从小到大的。
在这个输出里,会出现一个到两个下降,找到就好了。
空间可以用morris优化到O(1)。
100. Same Tree
开开心心hhh
101. Symmetric Tree
不记得题号了,有一道题就是这个里面的helper方法变种。
102. Binary Tree Level Order Traversal
queue和new_queue交换就好了。
BFS。
103. Binary Tree Zigzag Level Order Traversal
BFS,reverse在0和1摇摆就行了。
104. Maximum Depth of Binary Tree
开开心心hhh
105. Construct Binary Tree from Preorder and Inorder Traversal
复习一下。
基础写法就没写了。
def helper(base, start, end):
if start > end:
return None
root = TreeNode(preorder[start])
index = inorder[preorder[start]]
root.left = helper(base, start + 1, start + (index - base))
root.right = helper(index + 1, start + (index - base) + 1, end)
return root
106. Construct Binary Tree from Inorder and Postorder Traversal
和中序 + 前序一样的。
基础做法就是每次index,O(nlog(n))。
优化index,可以到O(n)。
我的做法是转index_dict,然后传入一个base,和post的start和end。
107. Binary Tree Level Order Traversal II
emmmm。 和LC 103的区别是啥。。
不过可以记一下dfs的写法。
108. Convert Sorted Array to Binary Search Tree
LC爸爸对我真好hhhh
找中点recursion就行了。
109. Convert Sorted List to Binary Search Tree
转化成上面的就可以hhh
或者实现middle,不过每次还要断开左右,比较繁琐。
110. Balanced Binary Tree
做到easy树,就是笑嘻嘻。
111. Minimum Depth of Binary Tree
BFS吧,DFS也差不多。
112. Path Sum
path的定义需要注意下。和上面的Lc 111一样,path有点坑。
模板就可以了。
113. Path Sum II
和上面一样的,不过返回值需要是当前节点的所有路径。
记得最后反转回来。
当然也可以不反转回来做。维护一个processing,用类似backtracking的方法做。
114. Flatten Binary Tree to Linked List
这道题有点意思哦。
可以改一改iterative preorder。
stack里面放right,就不放自己了。
115. Distinct Subsequences
可以用DP,当然也可以用memorization。
加一个last_index_dict,可以prunning很多backtracking步骤。
DP也能做呀。【这不是废话吗
dp[i][j] += dp[i - 1][j];
dp[i][j] += dp[i - 1][j - 1] if s[i - 1] == t[j - 1]
可以优化到 一维
116. Populating Next Right Pointers in Each Node
BFS不行,因为要space O(1)!
要space O(1)就一定要利用complete binary tree的性质。
每一层都是饱和的是解题的关键。【其实不是,只是简单了点】
117. Populating Next Right Pointers in Each Node II
和上面一道题有区别的地方,就是不是饱和的。
也就是说,得加一个找到下一层最左的部分。
上面一道题直接就是cur.left,这次我们要自己找。
使用pre来建立next连接会方便很多。
118. Pascal's Triangle
哈哈哈哈哈。
119. Pascal's Triangle II
要么直接生成,要么就迭代一下。
120. Triangle
从下向上每次俩俩选择min。
最后返回array[0]
121. Best Time to Buy and Sell Stock
和那道best sightview pair挺像的。
记录扫描到的最小值,然后和当前值进行运算,比较,更新。
122. Best Time to Buy and Sell Stock II
每有一个上升的pair,买!卖!
123. Best Time to Buy and Sell Stock III
神仙题目。真的。
一步一步优化到O(N), O(1)。
最基础的思路是DP。
124. Binary Tree Maximum Path Sum
还是可以用LEE的模板。
左右子树的结果如果小于0,就不要了,直接设为0。
125. Valid Palindrome
关键点只有一个。
python有 string.isalnum() 这个神奇的方法。
126. Word Ladder II
和下面一道题挺像的。
不过要求出来所有路径。
改一改bfs就好了。用dict来存到当前word的所有最短路径。
127. Word Ladder
【图】。
使用bfs找最短路径。关键在于如何构建图。
每次transform只有一个letter可以变,所以可以建立一个中间的dictionary。
比如 hot: *ot, h*t, ho*。 (这个做法还慢些,很神奇!)
也可以直接枚举小写字母。
优化:使用bidirectional BFS优化空间开销。
128. Longest Consecutive Sequence
sequence不要求连续,不要求顺序。
最简单肯定是sort后找。
对于这道题,用一个set来优化查找时间就好。
129. Sum Root to Leaf Numbers
preorder或者dfs一样的。
130. Surrounded Regions
我是真的讨厌写matrix的遍历!
131. Palindrome Partitioning
这道backtracking。真的写得我难受!
要想清楚如何避免重复:
必须要从start到i是回文才backtracking。
上面这个的复杂度,应该是O(N*2^N)。
我们来用一下马拉车算法。
我们用马拉车算法生成P数组,然后我们把P数组转成(start_index, max_length)构成的数组。
之后再用转化出来的数组,生成每一个下标index,可以和哪些right_index构成回文的数组 [set() for _ in range(len(s))]。
之后再用上面的backtracking就可以了。这个样子的复杂度是O(2^N),因为我们没有在回溯过程中找palindrome。
预处理的复杂度是O(N^2)。
不过这道题的testcase都很小,貌似 N <= 17,所以两个也没有啥区别。
132. Palindrome Partitioning II
直接用 LC.131 backtracking的方法也能过,可惜是900ms。
因为只需要求min_cut,所以其实不需要backtracking来遍历所有情况。
考虑一下DP,DP[i]代表s[:i]满足条件的min_cut,那么DP[i] = min(DP[j] + 1 for j in range(0, i)),相当于分成两个部分计算。
这要考虑的话,时间复杂度是O(N^2),使用从中心扩展的方法就可以了。
当然了,我们也可以使用马拉车来进一步优化,做法和 LC.131 的笔记都是一样的。
但是我们不需要最后的backtracking,更换为DP的做法。
for i, rights in enumerate(arr):
for right in rights:
DP[right] = min(DP[right], 1 + DP[i])
我们这个方法,最差是O(N^2),但是如果testcase不是一个字符从头到尾的话,其实inner loop是没有N的,平均时间应该是O(N^(3/2))。
看了discussion,还可以再优化一下。
反正也是O(N^2)嘛,所以可以试一下能否只砍一刀就完成任务。
133. Clone Graph
建一个mapping_dict。
不需要使用visited,只把不在dict里面的node加进queue就好了。
134. Gas Station
为什么。。为什么我的方法这么慢。。
官方答案:
核心要点:整体油量 >= 整体消耗的话,一定有solution。
这个就不证明了,太牛了。
我还他妈在那儿模拟呢
不过我模拟也是O(n)啊,为什么呢。
135. Candy
可以左右扫描一遍,然后用类似trapping water的做法做。不过是取max.
137. Single Number II
这道题,真的,我想不出来。
因为XOR可以统计出哪些bit出现了奇数次,所以我们要区分出现1次和3次的。
seen_once = ~seen_twice & (seen_once ^ num)
seen_twice = ~seen_once & (seen_twice ^ num)
139. Word Break
直接用DP。思路和cumulative sum非常相似。
DP[i] = True if DP[j] == True and s[j:i] in wordDict
还可以用BFS. (和BFS相比,这个用DFS更快)
140. Word Break II
用BFS或者DFS会超时。因为重复访问的东西太多了。
所以加一个memorization就好了。
有一个讨巧的优化是把所有words按照首字母分类保存。
这样判断的时候先判断首字母是否合格,会快一些。
142. Linked List Cycle II
龟兔做法。关键是找entry point。
设 起点 到 环开始点 为 L1; 环开始点 到 相遇点 为 L2。
环 的 长度 为 C
则 乌龟一共移动了 L1 + L2 + n1 * C
兔子一共移动了 L1 + L2 + n2 * C
因为 兔子的步数是乌龟的两倍,所以:
2 * (L1 + L2 + n1 * C) = L1 + L2 + n2 * C
L1 + L2 = (n2 - n1) * C
L1 = (n3 - 1) * C + (C - L2)
C - L2就是乌龟还需要走多久可以到 环开始点。
143. Reorder List
可以存在array里hhhhh
O(1)的做法是:找到中点,翻转后半部分,然后连接。
144. Binary Tree Preorder Traversal
有什么好说的呢?三种都写写。
145. Binary Tree Postorder Traversal
后序的iterative和Morris都是难点。时常复习是必要的。
147. Insertion Sort List
那些存在array里排序后重建的,人才。
建一个dummy = ListNode(-float('inf'))方便很多。
加一个last提高下速度。
148. Sort List
linked list sort。
merge sort实现一下 ok。
149. Max Points on a Line
有难度的一道题目。需要仔细想想。
尤其是如何表示一条直线,计算斜率的话,double可能精度不够导致错误。
使用GCD是个方法。
def GCD(a, b):
if a == 0: return b
return (b % a, a)
150. Evaluate Reverse Polish Notation
就一个stack。我写了。我过了。还要我怎么样。
151. Reverse Words in a String
用python做是一道弱智题目。
关键还是看看inplace + space O(1)的思路。
整体reverse后从左到右扫描,加一个valid pointer。
扫描到一个word后再次reverse这个word。
记得最后erase最后的部分。
153. Find Minimum in Rotated Sorted Array
直接的是O(n)。
巧妙一点的可以用二分法!!!
154. Find Minimum in Rotated Sorted Array II
一开始过滤下左边和结尾重复的元素。
156. Binary Tree Upside Down
个人感觉就是找规律。想了挺久的hhh
iterative的写法挺有意思。
temp = prev = curr = next = None
curr = root
while curr:
next = curr.left
curr.left = temp
temp = curr.right
curr.right = prev
prev = curr
curr = next
return prev
157. Read N Characters Given Read4
嗯??
158. Read N Characters Given Read4 II - Call multiple times
在上一道题的基础上,加一个buffer就好了。
159. Longest Substring with At Most Two Distinct Characters
和LC 904一模一样。
写法复习一下,挺简洁的。
a = b = ""
ans = cur = b_count = 0
for c in s:
cur = cur + 1 if c in (a, b) else b_count + 1
b_count = b_count + 1 if c == b else 1
if c != b:
a, b = b, c
ans = max(ans, cur)
161. One Edit Distance
直接用edit distance公式会超时。
那就分情况做呗!
162. Find Peak Element
这道题!!!
这就是魔法!!
这居然可以用binary search?????
为什么可以用二分法??
因为:
1.有一个假设,没有连续相同的值
2.并且假设了两边都是-inf
所以用二分法维护第二个性质,可以得到答案。
163. Missing Ranges
很迷的一道题,不知道这个英文描述是谁写的。
if解题就好了。如果处理遍历完后lower仍然小于upper?
把upper + 1放在末尾。
164. Maximum Gap
我只能想到sort了。
第二种方法就是Radix 基数排序了。
就是拿着每一位排序,从优先度低的一测还是排。
值得一提的是,Radix排序内的排序算法需要是【稳定】的。
这样做的话,因为数最大也就是int32,所以是O(10N)。
不过因为除法的开销其实并不是很低,所以也没有比python的timsort快。
另一种做法就是Bucket Sort了。
为了得到答案,我们只需要考虑桶内的最小距离和桶间的最大距离。
但是,桶内最小距离计算起来比较麻烦,所以有没有可能我们只需要考虑桶间距离呢?
问题的关键其实是 需要多少个桶!
根据鸽子洞原则,我们只需要用M个洞装N个鸽子,M>N。那么一定有空洞。
在这道题里面,我们只要保证左右两边的桶都有东西,空桶出现在中间,那我们就只需要考虑桶间距离了。
因为有空桶嘛,所以gap最小也得是一个桶的大小,这样我们就可以忽略桶间距离。
165. Compare Version Numbers
split + map(int)
166. Fraction to Recurring Decimal
这题完全就是自己大意了。
很多edge case都没有考虑过。
需要考虑负数的情况 --> 保证余数是正的。
如何判断循环?余数出现过多次就有循环了。
但是值得注意的是,需要保存余数对应的index,因为只要那部分到尾巴是循环部分。
167. Two Sum II - Input array is sorted
没有排序的题目,用了O(N)的dictionary来完成。
既然排序了,肯定可以优化下。
不用dictionnary,用two pointer左右就可以了。
168. Excel Sheet Column Title
进制问题老是想不明白。
这个问题没有0,所以(num - 1) % 26和(num - 1) // 26 (就是每次loop先减少1)
169. Majority Element
有挺多做法的。
count_dict; sort后取中间。
记一下摩尔投票。
还可以用bit运算做。就是每一个bit出现次数超过一半。
171. Excel Sheet Column Number
进制进制!
ans = ans * 26 + 1
ans += ord(c) - ord('A')
172. Factorial Trailing Zeroes
就是找这里面一共有多少个5。
我们不需要找2,因为2的个数一定是充足的。
注意下,25有两个5,125有三个5。
173. Binary Search Tree Iterator
把iterative in order tranversal 拆开就好了。
stack里面最多存O(h)个元素,满足条件。
174. Dungeon Game
DP做。没有特别想明白。
DP[i][j]描述的是 开局最少需要多少血 可以到dungeon[i][j]
dp[i][j] = max(1, min(dp[i+1][j], dp[i][j+1]) - dungeon[i][j])
179. Largest Number
学习了python3的functools.cmp_to_key。
自定义一下 comparator。
a + b 和 b + a这个写法好。
186. Reverse Words in a String II
要么直接join起来然后用一样的方法做。
要么就自己写一个inplace reverse的方法。
思路是全局reverse再单个单词reverse。
187. Repeated DNA Sequences
rolling hash的绝佳使用场景!
188. Best Time to Buy and Sell Stock IV
就是优化到一维DP。
要考虑特殊情况! K >= len(prices) // 2的话,相当于就是 LC.122
190. Reverse Bits
没啥好说的。CSAPP难度。跪了。
n = (n >> 16) | (n << 16)
n = ((n & 0xff00ff00) >> 8) | ((n & 0x00ff00ff) << 8)
n = ((n & 0xf0f0f0f0) >> 4) | ((n & 0x0f0f0f0f) << 4)
n = ((n & 0xcccccccc) >> 2) | ((n & 0x33333333) << 2)
n = ((n & 0xaaaaaaaa) >> 1) | ((n & 0x55555555) << 1)
191. Number of 1 Bits
有好多的办法呢!
str(n).count('1')是最简单的办法了。
如果用bit operation呢?
有一个trick是 num & (-num) 可以拿到 rightmost_bit。
当然了,如果只是要移除 rightmost_bit,可以直接用 num = num & (num - 1)
那如果连loop也不能用呢?
hamming weight就可以用了。
假设只是一个32-bit的数字。那么可以这么做:
num = (num & 0x55555555) + ((num & 0xaaaaaaaa) >> 1)
num = (num & 0x33333333) + ((num & 0xcccccccc) >> 2)
num = (num & 0x0f0f0f0f) + ((num & 0xf0f0f0f0) >> 4)
num = (num & 0x00ff00ff) + ((num & 0xff00ff00) >> 8)
num = (num & 0x0000ffff) + ((num & 0xffff0000) >> 16)