-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtree.json
More file actions
1642 lines (1642 loc) · 239 KB
/
Copy pathtree.json
File metadata and controls
1642 lines (1642 loc) · 239 KB
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
[
{
"name": "src",
"type": "directory",
"path": "src",
"children": [
{
"name": "1.数组",
"type": "directory",
"path": "src/1.数组",
"children": [
{
"name": "1.二分查找.ts",
"type": "file",
"path": "src/1.数组/1.二分查找.ts",
"content": "/**\n * @url https://leetcode.cn/problems/binary-search/description/\n */\nfunction search(nums: number[], target: number): number {\n let left = 0,\n right = nums.length - 1\n\n while (left <= right) {\n let mid = Math.floor((left + right) / 2)\n if (nums[mid] === target) return mid\n if (nums[mid] < target) {\n left = mid + 1\n }\n if (nums[mid] > target) {\n right = mid - 1\n }\n }\n return -1\n}\n\nconsole.log(search([1, 2, 3, 4, 5], 2))\n",
"url": "https://leetcode.cn/problems/binary-search/description/",
"sortKey": 1
},
{
"name": "2.移除元素.ts",
"type": "file",
"path": "src/1.数组/2.移除元素.ts",
"content": "/**\n * @url https://leetcode.cn/problems/remove-element/description/\n */\nfunction removeElement(nums: number[], val: number): number {\n let slow = 0,\n fast = 0;\n while (fast < nums.length) {\n nums[slow] = nums[fast]; // PS: 这里可以优化,不需要交换,直接覆盖即可。\n if (nums[fast] === val) {\n fast++;\n } else {\n slow++;\n fast++;\n }\n }\n return slow;\n}\nconsole.log(removeElement([3, 2, 2, 3], 3));\n",
"url": "https://leetcode.cn/problems/remove-element/description/",
"sortKey": 2
},
{
"name": "3.有序数组的平方.ts",
"type": "file",
"path": "src/1.数组/3.有序数组的平方.ts",
"content": "/**\n * @url https://leetcode.cn/problems/squares-of-a-sorted-array/description/\n */\nfunction sortedSquares(nums: number[]): number[] {\n const result: number[] = [];\n let left = 0,\n right = nums.length - 1;\n while (left <= right) {\n let leftRow = Math.pow(nums[left], 2),\n rightRow = Math.pow(nums[right], 2);\n if (leftRow <= rightRow) {\n result.unshift(rightRow);\n right--;\n } else {\n result.unshift(leftRow);\n left++;\n }\n }\n return result;\n}\nconsole.log(sortedSquares([-4, -1, 0, 3, 10]));\n",
"url": "https://leetcode.cn/problems/squares-of-a-sorted-array/description/",
"sortKey": 3
},
{
"name": "4.长度最小的子数组.ts",
"type": "file",
"path": "src/1.数组/4.长度最小的子数组.ts",
"content": "/**\n * @url https://leetcode.cn/problems/minimum-size-subarray-sum/\n */\nfunction minSubArrayLen(target: number, nums: number[]): number {\n let left = 0,\n res = Number.MAX_SAFE_INTEGER,\n sum = 0\n for (let i = 0; i < nums.length; i++) {\n sum += nums[i]\n while (sum >= target) {\n res = Math.min(i - left + 1, res)\n sum -= nums[left++]\n }\n }\n return res === Number.MAX_SAFE_INTEGER ? 0 : res\n}\n\n",
"url": "https://leetcode.cn/problems/minimum-size-subarray-sum/",
"sortKey": 4
},
{
"name": "5.螺旋矩阵2.ts",
"type": "file",
"path": "src/1.数组/5.螺旋矩阵2.ts",
"content": "/**\n * @url https://leetcode.cn/problems/spiral-matrix-ii/description/\n */\nfunction generateMatrix(n: number): number[][] {\n let top = 0,\n bottom = n - 1,\n left = 0,\n right = n - 1,\n count = 0, // 转几圈\n result = new Array(n).fill(0).map((_item) => new Array(n).fill(0)),\n num = 1\n while (count <= Math.floor(n / 2)) {\n // notice:执行一个循环需要做的事情\n for (let i = left; i <= right; i++) {\n result[top][i] = num++\n }\n top += 1\n for (let i = top; i <= bottom; i++) {\n result[i][right] = num++\n }\n right -= 1\n for (let i = right; i >= left; i--) {\n result[bottom][i] = num++\n }\n bottom -= 1\n for (let i = bottom; i >= top; i--) {\n result[i][left] = num++\n }\n left += 1\n count += 1\n }\n return result\n}\nconsole.table(generateMatrix(100))\n",
"url": "https://leetcode.cn/problems/spiral-matrix-ii/description/",
"sortKey": 5
}
],
"sortKey": 1
},
{
"name": "2.链表",
"type": "directory",
"path": "src/2.链表",
"children": [
{
"name": "1.移除链表元素.ts",
"type": "file",
"path": "src/2.链表/1.移除链表元素.ts",
"content": "/**\n * @url https://leetcode.cn/problems/remove-linked-list-elements/description/\n */\n\nclass ListNode {\n val: number\n next: ListNode | null\n constructor(val?: number, next?: null) {\n this.val = val === undefined ? 0 : val\n this.next = next === undefined ? null : next\n }\n}\n\nfunction removeElements(head: ListNode | null, val: number): ListNode | null {\n if (!head) return null\n let vitrualHead = new ListNode(0)\n vitrualHead.next = head\n let fast: ListNode | null = head,\n slow = vitrualHead\n while (fast) {\n if (fast.val === val) {\n slow.next = fast.next\n fast = fast.next\n } else {\n slow = fast\n fast = fast.next\n }\n }\n return vitrualHead.next\n}\n",
"url": "https://leetcode.cn/problems/remove-linked-list-elements/description/",
"sortKey": 1
},
{
"name": "2.设计链表.ts",
"type": "file",
"path": "src/2.链表/2.设计链表.ts",
"content": "/**\n * @url https://leetcode.cn/problems/design-linked-list/\n * @description 实现 MyLinkedList 类:\n MyLinkedList() 初始化 MyLinkedList 对象。\n int get(int index) 获取链表中下标为 index 的节点的值。如果下标无效,则返回 -1 。\n void addAtHead(int val) 将一个值为 val 的节点插入到链表中第一个元素之前。在插入完成后,新节点会成为链表的第一个节点。\n void addAtTail(int val) 将一个值为 val 的节点追加到链表中作为链表的最后一个元素。\n void addAtIndex(int index, int val) 将一个值为 val 的节点插入到链表中下标为 index 的节点之前。如果 index 等于链表的长度,那么该节点会被追加到链表的末尾。如果 index 比长度更大,该节点将 不会插入 到链表中。\n void deleteAtIndex(int index) 如果下标有效,则删除链表中下标为 index 的节点。\n */\n\nclass MyLinkedList {\n val: number\n next: null | MyLinkedList\n constructor(val: number, next: null | MyLinkedList) {\n this.val = val\n this.next = next\n }\n\n get(index: number): number {\n let count = 0,\n prev: MyLinkedList | null = this\n while (count <= index) {\n if (prev) {\n prev = prev.next\n }\n count++\n }\n if (prev) return prev.val\n return -1\n }\n\n addAtHead(val: number): void {\n let newHead = new MyLinkedList(val, this)\n this.next = newHead\n }\n\n addAtTail(val: number): void {\n let prev: MyLinkedList | null = this\n while (prev?.next) {\n prev = prev.next\n }\n let newHead = new MyLinkedList(val, null)\n prev.next = newHead\n }\n\n addAtIndex(index: number, val: number): void {\n let count = 0,\n prev: MyLinkedList | null = this,\n newHead = new MyLinkedList(val, null)\n while (count <= index) {\n if (prev) {\n prev = prev.next\n }\n count++\n }\n if (prev) {\n prev.next = newHead\n }\n }\n\n deleteAtIndex(index: number): void {\n let count = 0,\n prev: MyLinkedList | null = this\n while (count <= index - 1 && prev) {\n prev = prev.next\n count++\n }\n if (prev) {\n prev.next = !prev.next ? null : prev.next.next\n }\n }\n}\n\n/**\n * Your MyLinkedList object will be instantiated and called as such:\n * var obj = new MyLinkedList()\n * var param_1 = obj.get(index)\n * obj.addAtHead(val)\n * obj.addAtTail(val)\n * obj.addAtIndex(index,val)\n * obj.deleteAtIndex(index)\n */\n",
"url": "https://leetcode.cn/problems/design-linked-list/",
"sortKey": 2
},
{
"name": "3.反转链表.ts",
"type": "file",
"path": "src/2.链表/3.反转链表.ts",
"content": "/**\n * @url https://leetcode.cn/problesms/reverse-linked-list/description/\n */\n\nclass ListNode {\n val: number\n next: ListNode | null\n constructor(val?: number, next?: null) {\n this.val = val === undefined ? 0 : val\n this.next = next === undefined ? null : next\n }\n}\nfunction reverseList(head: ListNode | null): ListNode | null {\n if (!head) return null\n let pre = head,\n qo = head,\n fast = head.next\n while (fast && fast.next) {\n qo = fast\n fast = fast.next\n qo.next = pre\n pre = qo\n }\n return fast\n}\n\n// 1 2 3 4 5\n",
"url": "https://leetcode.cn/problesms/reverse-linked-list/description/",
"sortKey": 3
},
{
"name": "4.两两交换链表中的节点.ts",
"type": "file",
"path": "src/2.链表/4.两两交换链表中的节点.ts",
"content": "/**\n * @url https://leetcode.cn/problems/swap-nodes-in-pairs/description/\n */\n\nclass ListNode {\n val: number\n next: ListNode | null\n constructor(val?: number, next?: null) {\n this.val = val === undefined ? 0 : val\n this.next = next === undefined ? null : next\n }\n}\nexport {}\n\nfunction swapPairs(head: ListNode | null): ListNode | null {\n if (!head) return null\n if (!head.next) return head\n let vitrualNode: ListNode | null = new ListNode(0)\n vitrualNode.next = head\n let cur: ListNode | null = vitrualNode\n while (cur && cur.next && cur.next.next) {\n let p = cur.next,\n q = cur.next.next\n p.next = q.next\n q.next = p\n cur.next = q\n cur = cur.next!.next\n }\n return vitrualNode.next\n}\n\n// notice 要思考一个节点换位后前后关联的关系\n",
"url": "https://leetcode.cn/problems/swap-nodes-in-pairs/description/",
"sortKey": 4
},
{
"name": "5.删除链表的倒数第n个节点.ts",
"type": "file",
"path": "src/2.链表/5.删除链表的倒数第n个节点.ts",
"content": "// @ts-nocheck\n/**\n * @url https://leetcode.cn/problems/remove-nth-node-from-end-of-list/description/\n */\nclass ListNode {\n val: number;\n next: ListNode | null;\n constructor(val?: number, next?: null) {\n this.val = val === undefined ? 0 : val;\n this.next = next === undefined ? null : next;\n }\n}\nexport {};\n/**\n * Definition for singly-linked list.\n * function ListNode(val, next) {\n * this.val = (val===undefined ? 0 : val)\n * this.next = (next===undefined ? null : next)\n * }\n */\n/**\n * @param {ListNode} head\n * @param {number} n\n * @return {ListNode}\n */\n// v 1 1\nvar removeNthFromEnd = function (head, n) {\n const Vnode = new ListNode(0, head);\n let fast = Vnode;\n let count = 0;\n while (count < n) {\n fast = fast.next;\n count++;\n }\n let slow = Vnode;\n while (fast.next) {\n slow = slow.next;\n fast = fast.next;\n }\n slow.next = slow.next.next;\n return Vnode.next;\n};\n",
"url": "https://leetcode.cn/problems/remove-nth-node-from-end-of-list/description/",
"sortKey": 5
},
{
"name": "6.链表相交.js",
"type": "file",
"path": "src/2.链表/6.链表相交.js",
"content": "/**\n * @url https://leetcode.cn/problems/intersection-of-two-linked-lists-lcci/\n */\nfunction ListNode(val) {\n this.val = val\n this.next = null\n}\n\n/**\n * @param {ListNode} headA\n * @param {ListNode} headB\n * @return {ListNode}\n */\n\nconst getLens = (head) => {\n let count = 0,\n curNode = head\n while (curNode) {\n count++\n curNode = curNode.next\n }\n return count\n}\n\nvar getIntersectionNode = function (headA, headB) {\n let len1 = getLens(headA),\n len2 = getLens(headB),\n curNodeA = headA,\n curNodeB = headB,\n chazhi = Math.abs(len1 - len2),\n count = 0\n if (len1 > len2) {\n while (count < chazhi) {\n curNodeA = curNodeA.next\n count++\n }\n } else {\n while (count < chazhi) {\n curNodeB = curNodeB.next\n count++\n }\n }\n\n while (curNodeA) {\n if (curNodeA === curNodeB) return curNodeA\n curNodeA = curNodeA.next\n curNodeB = curNodeB.next\n }\n return null\n}\n",
"url": "https://leetcode.cn/problems/intersection-of-two-linked-lists-lcci/",
"sortKey": 6
},
{
"name": "7.环形链表.ts",
"type": "file",
"path": "src/2.链表/7.环形链表.ts",
"content": "/**\n * @url https://leetcode.cn/problems/linked-list-cycle-ii/\n */\n\nclass ListNode {\n val: number\n next: ListNode | null\n constructor(val?: number, next?: null) {\n this.val = val === undefined ? 0 : val\n this.next = next === undefined ? null : next\n }\n}\nfunction detectCycle(head: ListNode | null): ListNode | null {\n let slow: ListNode | null = head,\n fast: ListNode | null = head\n while (slow && fast && fast.next) {\n slow = slow.next\n fast = fast.next.next\n if (slow === fast) {\n // 这个时候是相遇点\n slow = head\n while (slow !== fast) {\n slow = slow!.next\n fast = fast!.next\n }\n return slow\n }\n }\n return null\n}\n",
"url": "https://leetcode.cn/problems/linked-list-cycle-ii/",
"sortKey": 7
},
{
"name": "8.判断链表是否有环.ts",
"type": "file",
"path": "src/2.链表/8.判断链表是否有环.ts",
"content": "/**\n * @url https://leetcode.cn/problems/linked-list-cycle/description/\n */\nexport {}\nclass ListNode {\n val: number\n next: ListNode | null\n constructor(val?: number, next?: null) {\n this.val = val === undefined ? 0 : val\n this.next = next === undefined ? null : next\n }\n}\nfunction hasCycle(head: ListNode | null): boolean {\n if (!head) return false\n if (!head.next) return false\n let slow: ListNode | null = head,\n fast: ListNode | null = head\n while (slow && fast) {\n slow = slow.next\n fast = fast.next?.next || null\n if (slow === fast) {\n return true\n }\n }\n return false\n}\n",
"url": "https://leetcode.cn/problems/linked-list-cycle/description/",
"sortKey": 8
},
{
"name": "9.合并两个有序链表.ts",
"type": "file",
"path": "src/2.链表/9.合并两个有序链表.ts",
"content": "/**\n * @url https://leetcode.cn/problems/merge-two-sorted-lists/description/\n */\nclass ListNode {\n val: number;\n next: ListNode | null;\n constructor(val?: number, next?: null) {\n this.val = val === undefined ? 0 : val;\n this.next = next === undefined ? null : next;\n }\n}\n\n// !分而治之中的治\nfunction mergeTwoLists(\n list1: ListNode | null,\n list2: ListNode | null\n): ListNode | null {\n let _vitrual = new ListNode(0); // 虚拟头结点\n let cur1 = list1,\n cur2 = list2,\n _head = _vitrual;\n\n while (cur1 && cur2) {\n if (cur1.val < cur2.val) {\n _head.next = cur1;\n cur1 = cur1.next;\n _head = _head.next;\n } else {\n _head.next = cur2;\n cur2 = cur2.next;\n _head = _head.next;\n }\n }\n if (cur1) {\n _head.next = cur1;\n cur1 = cur1.next;\n }\n if (cur2) {\n _head.next = cur2;\n cur2 = cur2.next;\n }\n return _vitrual.next;\n}\n",
"url": "https://leetcode.cn/problems/merge-two-sorted-lists/description/",
"sortKey": 9
},
{
"name": "10.合并k个有序链表.ts",
"type": "file",
"path": "src/2.链表/10.合并k个有序链表.ts",
"content": "/**\n * @url https://leetcode.cn/problems/merge-k-sorted-lists/description/\n */\n\nclass ListNode {\n val: number;\n next: ListNode | null;\n constructor(val?: number, next?: null) {\n this.val = val === undefined ? 0 : val;\n this.next = next === undefined ? null : next;\n }\n}\n\nfunction mergeTwoLists(\n list1: ListNode | null,\n list2: ListNode | null\n): ListNode | null {\n let _vitrual = new ListNode(0); // 虚拟头结点\n let cur1 = list1,\n cur2 = list2,\n _head = _vitrual;\n\n while (cur1 && cur2) {\n if (cur1.val < cur2.val) {\n _head.next = cur1;\n cur1 = cur1.next;\n _head = _head.next;\n } else {\n _head.next = cur2;\n cur2 = cur2.next;\n _head = _head.next;\n }\n }\n if (cur1) {\n _head.next = cur1;\n cur1 = cur1.next;\n }\n if (cur2) {\n _head.next = cur2;\n cur2 = cur2.next;\n }\n return _vitrual.next;\n}\n\n// function mergeKLists(lists: Array<ListNode | null>): ListNode | null {\n// if (lists.length === 0) return null\n// if (lists.length === 1) return lists[0]\n// let res: ListNode | null = null\n// for (let i = 0; i < lists.length; i++) {\n// res = mergeTwoLists(res, lists[i])\n// }\n// return res\n// }\n\nfunction mergeKLists(lists: Array<ListNode | null>): ListNode | null {\n if (lists.length === 0) return null;\n if (lists.length === 1) {\n return lists[0]; // ps:返回头结点\n }\n const midIndex = Math.floor(lists.length / 2);\n const left = lists.slice(0, midIndex + 1);\n const right = lists.slice(midIndex);\n return mergeTwoLists(mergeKLists(left), mergeKLists(right));\n}\n",
"url": "https://leetcode.cn/problems/merge-k-sorted-lists/description/",
"sortKey": 10
},
{
"name": "11.反转k个一组链表.ts",
"type": "file",
"path": "src/2.链表/11.反转k个一组链表.ts",
"content": "// @ts-nocheck\n/**\n * @url https://leetcode.cn/problems/reverse-nodes-in-k-group/\n */\n/**\n * Definition for singly-linked list.\n * function ListNode(val, next) {\n * this.val = (val===undefined ? 0 : val)\n * this.next = (next===undefined ? null : next)\n * }\n */\n/**\n * @param {ListNode} head\n * @param {number} k\n * @return {ListNode}\n */\n\nfunction ReverseHeadAndTail(head, tail) {\n let pre = null,\n cur = null,\n fast = head;\n while (fast !== tail) {\n cur = fast;\n fast = fast.next;\n cur.next = pre;\n pre = cur;\n }\n if (fast) {\n cur = fast;\n cur.next = pre;\n }\n return [cur, head];\n}\n\nvar reverseKGroup = function (head, k) {\n const _VNode = new ListNode(0);\n _VNode.next = head;\n let fast = _VNode;\n while (fast) {\n let p = fast; // 翻转前的一个节点\n let count = 0;\n let tail = fast; // 要翻转链表的最后一个节点\n while (count < k && tail) {\n tail = tail.next;\n count++;\n }\n if (k === count && tail) {\n const tailNext = tail.next;\n tail.next = null;\n const [reverseHead, reverseTail] = ReverseHeadAndTail(p.next, tail);\n p.next = reverseHead;\n reverseTail.next = tailNext;\n fast = reverseTail;\n } else {\n break; // ps: 不足k个直接退出循环了。\n }\n }\n return _VNode.next;\n};\n",
"url": "https://leetcode.cn/problems/reverse-nodes-in-k-group/",
"sortKey": 11
}
],
"sortKey": 2
},
{
"name": "3.哈希表",
"type": "directory",
"path": "src/3.哈希表",
"children": [
{
"name": "1.有效的字母异位词.ts",
"type": "file",
"path": "src/3.哈希表/1.有效的字母异位词.ts",
"content": "/**\n * @url https://leetcode.cn/problems/valid-anagram/description/\n */\n\nfunction isAnagram(s: string, t: string): boolean {\n const mapS = new Map()\n for (let i = 0; i < s.length; i++) {\n if (mapS.has(s[i])) {\n mapS.set(s[i], mapS.get(s[i]) + 1)\n } else {\n mapS.set(s[i], 1)\n }\n }\n for (let i = 0; i < t.length; i++) {\n if (mapS.has(t[i])) {\n mapS.set(t[i], mapS.get(t[i]) - 1)\n } else {\n return false\n }\n }\n\n for (let value of mapS.values()) {\n if (value !== 0) return false\n }\n return true\n}\n// notice:使用Map能够覆盖所有情况的字母异味词\n",
"url": "https://leetcode.cn/problems/valid-anagram/description/",
"sortKey": 1
},
{
"name": "2.两个数组的交集.ts",
"type": "file",
"path": "src/3.哈希表/2.两个数组的交集.ts",
"content": "/**\n * @url https://leetcode.cn/problems/intersection-of-two-arrays/description/\n */\nfunction intersection(nums1: number[], nums2: number[]): number[] {\n const set = new Set()\n const set1 = new Set()\n nums1.forEach((_num) => set.add(_num))\n nums2.forEach((_num) => {\n if (set.has(_num)) {\n set1.add(_num)\n }\n })\n return Array.from(set1) as number[]\n}\n\n// notice:在add的时候就已经去重了\n",
"url": "https://leetcode.cn/problems/intersection-of-two-arrays/description/",
"sortKey": 2
},
{
"name": "3.快乐数.ts",
"type": "file",
"path": "src/3.哈希表/3.快乐数.ts",
"content": "/**\n * @url https://leetcode.cn/problems/happy-number/description/\n */\n\n// notice:取模技巧 从个位开始取\nconst getNext = (n) => {\n let sum = 0;\n while (n > 0) {\n const digit = n % 10;\n sum += digit * digit;\n n = Math.floor(n / 10);\n }\n return sum;\n};\n\nconst getSqrtNum = (num: string) => {\n let sum = 0;\n for (let i = 0; i < num.length; i++) {\n sum += Math.pow(Number(num[i]), 2);\n }\n return sum;\n};\nfunction isHappy(n: number): boolean {\n const set = new Set();\n while (true) {\n if (n === 1) {\n return true;\n }\n if (set.has(n)) return false;\n set.add(n);\n n = getSqrtNum(n.toString());\n }\n}\n",
"url": "https://leetcode.cn/problems/happy-number/description/",
"sortKey": 3
},
{
"name": "4.两数之和.ts",
"type": "file",
"path": "src/3.哈希表/4.两数之和.ts",
"content": "/**\n * @url https://leetcode.cn/problems/two-sum/description/\n */\nfunction twoSum(nums: number[], target: number): number[] {\n const map = new Map()\n for (let index = 0; index < nums.length; index++) {\n if (map.has(target - nums[index])) {\n return [index, map.get(target - nums[index])]\n }\n map.set(nums[index], index)\n }\n return [-1, -1]\n}\n",
"url": "https://leetcode.cn/problems/two-sum/description/",
"sortKey": 4
},
{
"name": "5.四数之和.ts",
"type": "file",
"path": "src/3.哈希表/5.四数之和.ts",
"content": "/**\n * @url https://leetcode.cn/problems/4sum-ii/description/\n */\n// function fourSumCount(nums1: number[], nums2: number[], nums3: number[], nums4: number[]): number {\n// let res1: number[] = [],\n// res2: number[] = [],\n// count = 0\n// for (let i = 0; i < nums1.length; i++) {\n// for (let j = 0; j < nums2.length; j++) {\n// res1.push(nums1[i] + nums2[j])\n// }\n// }\n// for (let i = 0; i < nums3.length; i++) {\n// for (let j = 0; j < nums4.length; j++) {\n// res2.push(nums3[i] + nums4[j])\n// }\n// }\n\n// for (let i = 0; i < res1.length; i++) {\n// for (let j = 0; j < res2.length; j++) {\n// if (res1[i] + res2[j] === 0) {\n// count++\n// }\n// }\n// }\n// return count\n// }\n\nfunction fourSumCount(\n nums1: number[],\n nums2: number[],\n nums3: number[],\n nums4: number[]\n): number {\n let map1 = new Map(),\n map2 = new Map(),\n count = 0;\n\n for (let i = 0; i < nums1.length; i++) {\n for (let j = 0; j < nums2.length; j++) {\n let sum = nums1[i] + nums2[j];\n if (map1.has(sum)) {\n map1.set(sum, map1.get(sum) + 1);\n } else {\n map1.set(sum, 1);\n }\n }\n }\n\n for (let i = 0; i < nums3.length; i++) {\n for (let j = 0; j < nums4.length; j++) {\n let sum = nums3[i] + nums4[j];\n if (map2.has(sum)) {\n map2.set(sum, map2.get(sum) + 1);\n } else {\n map2.set(sum, 1);\n }\n }\n }\n\n for (let [key1, value1] of map1.entries()) {\n for (let [key2, value2] of map2.entries()) {\n if (key1 + key2 === 0) {\n count += value1 * value2;\n }\n }\n }\n\n return count;\n}\n// notice:用数组来比较,最后会展示超时,优化的点就是利用Map减少最后遍历的次数\n",
"url": "https://leetcode.cn/problems/4sum-ii/description/",
"sortKey": 5
},
{
"name": "6.赎金信.ts",
"type": "file",
"path": "src/3.哈希表/6.赎金信.ts",
"content": "/**\n * @url https://leetcode.cn/problems/ransom-note/description/\n */\nfunction canConstruct(ransomNote: string, magazine: string): boolean {\n let arr = new Array(26).fill(0)\n for (let index = 0; index < magazine.length; index++) {\n arr[magazine[index].charCodeAt(0) - \"a\".charCodeAt(0)]++\n }\n for (let index = 0; index < ransomNote.length; index++) {\n arr[ransomNote[index].charCodeAt(0) - \"a\".charCodeAt(0)]--\n }\n for (let index = 0; index < arr.length; index++) {\n if (arr[index] < 0) {\n return false\n }\n }\n return true\n}\n",
"url": "https://leetcode.cn/problems/ransom-note/description/",
"sortKey": 6
},
{
"name": "7.三数之和.ts",
"type": "file",
"path": "src/3.哈希表/7.三数之和.ts",
"content": "/**\n * @url https://leetcode.cn/problems/3sum/description/\n */\n\n// -1 -1 -1 0 1 2\nfunction threeSum(nums: number[]): number[][] {\n nums.sort((a, b) => a - b)\n const res: number[][] = []\n\n for (let i = 0; i < nums.length - 2; i++) {\n if (i > 0 && nums[i] === nums[i - 1]) {\n continue\n }\n let left = i + 1,\n right = nums.length - 1\n while (left < right) {\n let sum = nums[i] + nums[left] + nums[right]\n if (sum === 0) {\n while (nums[left] === nums[left + 1]) {\n left++\n }\n while (nums[right] === nums[right - 1]) {\n right--\n }\n res.push([nums[i], nums[left], nums[right]])\n right--\n left++\n } else if (sum < 0) {\n left++\n } else {\n right--\n }\n }\n }\n\n return res\n}\n\n// notice:固定两边移动之间是可以覆盖全的,排列组合计算方式\n// todo:难点,关于找到后去重\n",
"url": "https://leetcode.cn/problems/3sum/description/",
"sortKey": 7
},
{
"name": "8.四数之和.ts",
"type": "file",
"path": "src/3.哈希表/8.四数之和.ts",
"content": "/**\n * @url https://leetcode.cn/problems/4sum/\n */\n// 这里的不重复是指值不能重复\n// 2 2 2 1 1\nfunction fourSum(nums: number[], target: number): number[][] {\n const res: number[][] = []\n nums.sort((a, b) => a - b)\n for (let i = 0; i < nums.length - 3; i++) {\n if (i > 0 && nums[i - 1] === nums[i]) {\n continue\n }\n for (let j = i + 1; j < nums.length - 2; j++) {\n if (j > i + 1 && nums[j] === nums[j - 1]) continue // 如果是初始给过滤,那么将不会获取到这个值\n let left = j + 1,\n right = nums.length - 1\n while (left < right) {\n let sum = nums[left] + nums[right] + nums[i] + nums[j]\n if (sum === target) {\n res.push([nums[left], nums[right], nums[i], nums[j]])\n while (nums[left] === nums[left + 1]) {\n left++\n }\n while (nums[right] === nums[right - 1]) {\n right--\n }\n left++\n right--\n } else if (sum > target) {\n right--\n } else {\n left++\n }\n }\n }\n }\n return res\n}\n\n// -2 -1 0 0 1 2\n",
"url": "https://leetcode.cn/problems/4sum/",
"sortKey": 8
}
],
"sortKey": 3
},
{
"name": "4.字符串",
"type": "directory",
"path": "src/4.字符串",
"children": [
{
"name": "1.反转字符串.ts",
"type": "file",
"path": "src/4.字符串/1.反转字符串.ts",
"content": "/**\n * @url https://leetcode.cn/problems/reverse-string/\n */\n\nfunction reverseString(s: string[]): void {\n let left = 0,\n right = s.length - 1\n while (right > left) {\n ;[s[right], s[left]] = [s[left], s[right]]\n right--\n left++\n }\n}\n",
"url": "https://leetcode.cn/problems/reverse-string/",
"sortKey": 1
},
{
"name": "2.反转字符串二.ts",
"type": "file",
"path": "src/4.字符串/2.反转字符串二.ts",
"content": "/**\n * @url https://leetcode.cn/problems/reverse-string-ii/\n */\n\n// 字符串的长度为j-i+1 i+k其实就是索引在原先的基础上去加一了。所以这里判断的时候也就是判断index+k>s.length了\nfunction reverseStr(s: string, k: number): string {\n for (let index = 0; index < s.length; index += 2 * k) {\n if (index + k > s.length) {\n s =\n s.slice(0, index) +\n s\n .slice(index + k)\n .split(\"\")\n .reverse()\n .join(\"\")\n } else {\n s =\n s.slice(0, index) +\n s\n .slice(index, index + k)\n .split(\"\")\n .reverse()\n .join(\"\") +\n s.slice(index + k)\n }\n }\n return s\n}\n\n// notice:主要坑点注意下标位置 index 后面的 index + k\n// notice:index 后面的 index + k\n",
"url": "https://leetcode.cn/problems/reverse-string-ii/",
"sortKey": 2
},
{
"name": "3.反转字符串中的单词.ts",
"type": "file",
"path": "src/4.字符串/3.反转字符串中的单词.ts",
"content": "/**\n * @url https://leetcode.cn/problems/reverse-words-in-a-string/description/\n */\n\n// todo:获取字符串的单词\n// todo:死循环?不太可能,因为每一次循环都会走一次判空和非判空的处理。\nconst getTokens = (s: string) => {\n let trimS = s.trim(),\n left = trimS.length - 1,\n right = trimS.length - 1,\n words: Array<string> = []\n while (left >= 0) {\n while (left >= 0 && trimS[left] !== \" \") {\n left--\n }\n words.push(trimS.slice(left + 1, right + 1))\n while (left >= 0 && trimS[left] === \" \") {\n left--\n }\n right = left\n }\n return words\n}\n\nfunction joinTokens(words: Array<string>) {\n return words\n .reduce((res, _cur) => {\n return res + \" \" + _cur\n }, \"\")\n .trimStart()\n}\n\nfunction reverseWords(s: string): string {\n return joinTokens(getTokens(s))\n}\n",
"url": "https://leetcode.cn/problems/reverse-words-in-a-string/description/",
"sortKey": 3
},
{
"name": "4.实现strStr().ts",
"type": "file",
"path": "src/4.字符串/4.实现strStr().ts",
"content": "/**\n * @url https://leetcode.cn/problems/find-the-index-of-the-first-occurrence-in-a-string/description/\n * @description 实际考察kmp算法,// todo\n */\nfunction strStr(haystack: string, needle: string): number {\n return haystack.indexOf(needle)\n}\n",
"url": "https://leetcode.cn/problems/find-the-index-of-the-first-occurrence-in-a-string/description/",
"sortKey": 4
},
{
"name": "5.重复的子字符串.ts",
"type": "file",
"path": "src/4.字符串/5.重复的子字符串.ts",
"content": "/**\n * @url https://leetcode.cn/problems/repeated-substring-pattern/description/\n */\nfunction repeatedSubstringPattern(s: string): boolean {\n for (let i = 0; i < Math.floor(s.length / 2); i++) {\n let len = Math.floor(s.length / s.slice(0, i + 1).length)\n if (s.slice(0, i + 1).repeat(len) === s) return true\n }\n return false\n}\n",
"url": "https://leetcode.cn/problems/repeated-substring-pattern/description/",
"sortKey": 5
}
],
"sortKey": 4
},
{
"name": "5.栈与队列",
"type": "directory",
"path": "src/5.栈与队列",
"children": [
{
"name": "1.用栈实现队列.ts",
"type": "file",
"path": "src/5.栈与队列/1.用栈实现队列.ts",
"content": "/**\n * @url https://leetcode.cn/problems/implement-queue-using-stacks/description/\n */\nclass MyQueue {\n queue: Array<number>\n constructor() {\n this.queue = []\n }\n\n push(x: number): void {\n this.queue.push(x)\n }\n\n pop(): number {\n return this.queue.shift() || 0\n }\n\n peek(): number {\n return this.queue.concat([]).shift() || 0\n }\n\n empty(): boolean {\n return this.queue.length === 0\n }\n}\n\n/**\n * Your MyQueue object will be instantiated and called as such:\n * var obj = new MyQueue()\n * obj.push(x)\n * var param_2 = obj.pop()\n * var param_3 = obj.peek()\n * var param_4 = obj.empty()\n */\n",
"url": "https://leetcode.cn/problems/implement-queue-using-stacks/description/",
"sortKey": 1
},
{
"name": "2.用队列实现栈.ts",
"type": "file",
"path": "src/5.栈与队列/2.用队列实现栈.ts",
"content": "/**\n * @url https://leetcode.cn/problems/implement-stack-using-queues/description/\n */\n\nclass MyStack {\n queue: Array<number>\n constructor() {\n this.queue = []\n }\n\n push(x: number): void {\n this.queue.push(x)\n }\n\n pop(): number {\n return this.queue.pop() || -1\n }\n\n top(): number {\n return this.queue.concat([]).pop() || -1\n }\n\n empty(): boolean {\n return this.queue.length === 0\n }\n}\n\n/**\n * Your MyStack object will be instantiated and called as such:\n * var obj = new MyStack()\n * obj.push(x)\n * var param_2 = obj.pop()\n * var param_3 = obj.top()\n * var param_4 = obj.empty()\n */\n",
"url": "https://leetcode.cn/problems/implement-stack-using-queues/description/",
"sortKey": 2
},
{
"name": "3.有效的括号.ts",
"type": "file",
"path": "src/5.栈与队列/3.有效的括号.ts",
"content": "/**\n * @url https://leetcode.cn/problems/valid-parentheses/\n */\nfunction isValid(s: string): boolean {\n let obj = {\n \")\": \"(\",\n \"}\": \"{\",\n \"]\": \"[\",\n },\n stack: Array<string> = []\n for (let i = 0; i < s.length; i++) {\n if (obj[s[i]] && stack.length > 0) {\n const top = stack[stack.length - 1]\n if (top === obj[s[i]]) {\n stack.pop()\n } else {\n stack.push(s[i])\n }\n } else {\n stack.push(s[i])\n }\n }\n\n return stack.length === 0\n}\n",
"url": "https://leetcode.cn/problems/valid-parentheses/",
"sortKey": 3
},
{
"name": "4.删除字符串中的所有相邻重复项.ts",
"type": "file",
"path": "src/5.栈与队列/4.删除字符串中的所有相邻重复项.ts",
"content": "/**\n * @url https://leetcode.cn/problems/remove-all-adjacent-duplicates-in-string/description/\n */\nfunction removeDuplicates(s: string): string {\n const sArray = s.split(\"\")\n let stack: Array<string> = []\n for (let i = 0; i < sArray.length; i++) {\n if (stack.length === 0) {\n stack.push(sArray[i])\n } else {\n let top = stack[stack.length - 1]\n // let top = stack.concat().pop() // todo 这样会超时\n if (sArray[i] === top) {\n stack.pop()\n } else {\n stack.push(sArray[i])\n }\n }\n }\n return stack.join(\"\")\n}\n// notice:超时我就不是很理解\n",
"url": "https://leetcode.cn/problems/remove-all-adjacent-duplicates-in-string/description/",
"sortKey": 4
},
{
"name": "5.逆波兰表达式求值.ts",
"type": "file",
"path": "src/5.栈与队列/5.逆波兰表达式求值.ts",
"content": "/**\n * @url https://leetcode.cn/problems/evaluate-reverse-polish-notation/description/\n */\n\nconst operators = [\"+\", \"-\", \"*\", \"/\"]\ntype computeType = \"+\" | \"-\" | \"*\" | \"/\"\nconst compute = (operators: computeType, top1: string, top2: string) => {\n switch (operators) {\n case \"+\":\n return String(Number(top1) + Number(top2))\n case \"-\":\n return String(Number(top1) - Number(top2))\n case \"*\":\n return String(Number(top1) * Number(top2))\n case \"/\":\n return String(Number.parseInt(String(Number(top1) / Number(top2)))) //notice: Math.floor向下取整,注意负数的形式。 -0.4 会取整为-1\n }\n}\n\nfunction evalRPN(tokens: string[]): number {\n let stack: Array<string> = [] // 存储栈\n for (let i = 0; i < tokens.length; i++) {\n console.log(\"tokens\", stack)\n\n if (operators.includes(tokens[i])) {\n if (stack.length < 2) {\n stack.push(tokens[i])\n } else {\n const top2 = stack.pop()\n const top1 = stack.pop()\n stack.push(compute(tokens[i] as computeType, top1!, top2!))\n }\n } else {\n stack.push(tokens[i])\n }\n }\n return Number(stack.pop())\n}\n\n// console.log(evalRPN([\"2\", \"1\", \"+\", \"3\", \"*\"]))\n// console.log(evalRPN([\"4\", \"13\", \"5\", \"/\", \"+\"]))\n",
"url": "https://leetcode.cn/problems/evaluate-reverse-polish-notation/description/",
"sortKey": 5
},
{
"name": "6.滑动窗口求最大值.todo.ts",
"type": "file",
"path": "src/5.栈与队列/6.滑动窗口求最大值.todo.ts",
"content": "/**\n * @url https://leetcode.cn/problems/sliding-window-maximum/description/\n */\n",
"url": "https://leetcode.cn/problems/sliding-window-maximum/description/",
"sortKey": 6
},
{
"name": "7.前k个高频元素.todo.ts",
"type": "file",
"path": "src/5.栈与队列/7.前k个高频元素.todo.ts",
"content": "/**\n * @url https://leetcode.cn/problems/top-k-frequent-elements/description/\n */\n",
"url": "https://leetcode.cn/problems/top-k-frequent-elements/description/",
"sortKey": 7
}
],
"sortKey": 5
},
{
"name": "6.二叉树",
"type": "directory",
"path": "src/6.二叉树",
"children": [
{
"name": "1.二叉树的递归遍历.ts",
"type": "file",
"path": "src/6.二叉树/1.二叉树的递归遍历.ts",
"content": "/**\n * @description 前序遍历\n * @url https://leetcode.cn/problems/binary-tree-preorder-traversal/description/\n *\n * @description 中序遍历\n * @url https://leetcode.cn/problems/binary-tree-inorder-traversal/description/\n *\n * @description 后序遍历\n * @url https://leetcode.cn/problems/binary-tree-postorder-traversal/description/\n */\n\nclass TreeNode {\n val: number\n left: TreeNode | null\n right: TreeNode | null\n constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {\n this.val = val === undefined ? 0 : val\n this.left = left === undefined ? null : left\n this.right = right === undefined ? null : right\n }\n}\n\n/** 前序遍历 */\nfunction preorderTraversal(root: TreeNode | null): number[] {\n let stack: number[] = []\n if (!root) return stack\n stack.push(root.val)\n stack.push(...preorderTraversal(root.left))\n stack.push(...preorderTraversal(root.right))\n return stack\n}\n\n/** 中序遍历 */\nfunction inorderTraversal(root: TreeNode | null): number[] {\n let stack: number[] = []\n if (!root) return stack\n stack.push(...inorderTraversal(root.left))\n stack.push(root.val)\n stack.push(...inorderTraversal(root.right))\n return stack\n}\n\n/** 后续遍历 */\nfunction postorderTraversal(root: TreeNode | null): number[] {\n let stack: number[] = []\n if (!root) return stack\n stack.push(...postorderTraversal(root.left))\n stack.push(...postorderTraversal(root.right))\n stack.push(root.val)\n return stack\n}\n",
"url": "https://leetcode.cn/problems/binary-tree-preorder-traversal/description/",
"sortKey": 1
},
{
"name": "2.二叉树的迭代遍历.ts",
"type": "file",
"path": "src/6.二叉树/2.二叉树的迭代遍历.ts",
"content": "/**\n * @description 迭代遍历\n * @url url是1中的\n */\n\n// notice:处理节点和遍历节点\n\nclass TreeNode {\n val: number\n left: TreeNode | null\n right: TreeNode | null\n constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {\n this.val = val === undefined ? 0 : val\n this.left = left === undefined ? null : left\n this.right = right === undefined ? null : right\n }\n}\n\nlet root = new TreeNode(1, null, new TreeNode(2, new TreeNode(3), null))\n\n/** 前序遍历 */\nfunction preorderTraversal(root: TreeNode | null): number[] {\n let stack: Array<TreeNode | null> = [],\n result: number[] = []\n if (!root) return result\n stack.push(root)\n while (stack.length) {\n let top = stack.pop()\n if (top) {\n result.push(top.val)\n stack.push(top.right)\n stack.push(top.left)\n }\n }\n return result\n}\n\n/** 中序遍历 */\n// 左中右\n// TODO:可以再来尝试一下\nfunction inorderTraversal(root: TreeNode | null): number[] {\n let stack: Array<TreeNode | null> = [],\n result: number[] = []\n if (!root) return result\n while (root || stack.length !== 0) {\n if (root) {\n stack.push(root)\n root = root?.left\n } else {\n let node = stack.pop()\n root = node || null\n if (node) {\n result.push(node.val)\n }\n root = root?.right || null\n }\n }\n\n return result\n}\n\n/** 后续遍历 */\n// TODO:先写前序,改变前序的顺序,然后反转前序遍历的数组\nfunction postorderTraversal(root: TreeNode | null): number[] {\n let stack: Array<TreeNode | null> = [],\n result: number[] = []\n if (!root) return result\n stack.push(root)\n while (stack.length) {\n let top = stack.pop()\n if (top) {\n result.push(top?.val)\n stack.push(top.left)\n stack.push(top.right)\n }\n }\n return result.reverse()\n}\n",
"url": "",
"sortKey": 2
},
{
"name": "3.二叉树的层序遍历.ts",
"type": "file",
"path": "src/6.二叉树/3.二叉树的层序遍历.ts",
"content": "/**\n * @url https://leetcode.cn/problems/binary-tree-level-order-traversal/description/\n */\n// 层序遍历用队列\nfunction levelOrder(root: TreeNode | null): number[][] {\n let stack: Array<TreeNode | null> = [],\n result: number[][] = []\n if (!root) return result\n stack.push(root)\n while (stack.length) {\n const copyStack = stack.concat([])\n const res: number[] = []\n for (let i = 0; i < copyStack.length; i++) {\n const queue = stack.shift()\n if (queue) {\n res.push(queue.val)\n }\n if (queue?.left) {\n stack.push(queue.left)\n }\n if (queue?.right) {\n stack.push(queue.right)\n }\n }\n result.push(res)\n }\n return result\n}\n\n/**\n * @url https://leetcode.cn/problems/binary-tree-level-order-traversal-ii/description/\n */\nfunction levelOrderBottom(root: TreeNode | null): number[][] {\n let stack: Array<TreeNode | null> = [],\n result: number[][] = []\n if (!root) return result\n stack.push(root)\n while (stack.length) {\n let traverseStack = stack.concat(),\n temRes: number[] = []\n for (let i = 0; i < traverseStack.length; i++) {\n let top = stack.shift()\n if (top) {\n temRes.push(top?.val)\n }\n if (top?.left) {\n stack.push(top.left)\n }\n if (top?.right) {\n stack.push(top.right)\n }\n }\n result.push(temRes)\n }\n return result.reverse()\n}\n\n/**\n * @url https://leetcode.cn/problems/binary-tree-right-side-view/\n */\nfunction rightSideView(root: TreeNode | null): number[] {\n let stack: Array<TreeNode | null> = [],\n result: number[] = []\n if (!root) return result\n stack.push(root)\n while (stack.length) {\n let traverseStack = stack.concat(),\n temRes: number[] = []\n for (let i = 0; i < traverseStack.length; i++) {\n let top = stack.shift()\n if (top) {\n temRes.push(top?.val)\n }\n if (top?.left) {\n stack.push(top.left)\n }\n if (top?.right) {\n stack.push(top.right)\n }\n }\n result.push(temRes[temRes.length - 1])\n }\n return result\n}\n\n/**\n * @url https://leetcode.cn/problems/average-of-levels-in-binary-tree/description/\n */\nfunction averageOfLevels(root: TreeNode | null): number[] {\n let stack: Array<TreeNode | null> = [],\n result: number[] = []\n if (!root) return result\n stack.push(root)\n while (stack.length) {\n let traverseStack = stack.concat(),\n temRes: number[] = []\n for (let i = 0; i < traverseStack.length; i++) {\n let top = stack.shift()\n if (top) {\n temRes.push(top?.val)\n }\n if (top?.left) {\n stack.push(top.left)\n }\n if (top?.right) {\n stack.push(top.right)\n }\n }\n result.push(\n temRes.reduce((acc, cur) => {\n return acc + cur\n }, 0) / temRes.length\n )\n }\n return result\n}\n\n/**\n * @url https://leetcode.cn/problems/n-ary-tree-level-order-traversal/description/\n */\n\n// class _Node {\n// val: number\n// children: _Node[]\n// constructor(v: number) {\n// this.val = v\n// this.children = []\n// }\n// }\n\n// function levelOrderN(root: _Node | null): number[][] {\n// let stack: Array<_Node | null> = [],\n// result: number[][] = []\n// if (!root) return result\n// stack.push(root)\n// while (stack.length) {\n// let traverseStack = stack.concat(),\n// temRes: number[] = []\n// for (let i = 0; i < traverseStack.length; i++) {\n// let top = stack.shift()\n// if (top) {\n// temRes.push(top?.val)\n// }\n// if (top?.children.length) {\n// top.children.forEach((_childNode) => {\n// stack.push(_childNode)\n// })\n// }\n// }\n// result.push(temRes)\n// }\n// return result\n// }\n\n/**\n * @description 在每个树行中找最大值\n * @url https://leetcode.cn/problems/find-largest-value-in-each-tree-row/description/\n */\n\nfunction largestValues(root: TreeNode | null): number[] {\n let stack: Array<TreeNode | null> = [],\n result: number[] = []\n if (!root) return result\n stack.push(root)\n while (stack.length) {\n let traverseStack = stack.concat(),\n temRes: number[] = []\n for (let i = 0; i < traverseStack.length; i++) {\n let top = stack.shift()\n if (top) {\n temRes.push(top?.val)\n }\n if (top?.left) {\n stack.push(top.left)\n }\n if (top?.right) {\n stack.push(top.right)\n }\n }\n result.push(Math.max(...temRes))\n }\n return result\n}\n\nclass _Node {\n val: number\n left: _Node | null\n right: _Node | null\n next: _Node | null\n constructor(val?: number, left?: _Node, right?: _Node, next?: _Node) {\n this.val = val === undefined ? 0 : val\n this.left = left === undefined ? null : left\n this.right = right === undefined ? null : right\n this.next = next === undefined ? null : next\n }\n}\n/**\n * @description 填充每个节点的下一个右侧节点指针\n * @url https://leetcode.cn/problems/populating-next-right-pointers-in-each-node/description/\n */\nfunction connect(root: _Node | null): _Node | null {\n let stack: Array<_Node | null> = []\n if (!root) return root\n stack.push(root)\n while (stack.length) {\n let tempQueue = stack.concat()\n for (let i = 0; i < tempQueue.length; i++) {\n let _node = stack.shift()\n if (tempQueue.length !== 1) {\n if (i !== tempQueue.length - 1) {\n _node!.next = stack[0]\n }\n }\n if (_node?.left) {\n stack.push(_node?.left)\n }\n if (_node?.right) {\n stack.push(_node?.right)\n }\n }\n }\n return root\n}\n\n/**\n * @description 最大深度\n * @url https://leetcode.cn/problems/maximum-depth-of-binary-tree/description/\n */\nfunction maxDepth(root: TreeNode | null): number {\n let stack: Array<TreeNode | null> = [],\n depth = 0\n if (!root) return depth\n stack.push(root)\n while (stack.length) {\n let tempStack = stack.concat()\n for (let i = 0; i < tempStack.length; i++) {\n let node = stack.pop()\n node?.left && stack.push(node.left)\n node?.right && stack.push(node.right)\n }\n depth += 1\n }\n return depth\n}\n\n/**\n * @description 最小深度\n * @url https://leetcode.cn/problems/minimum-depth-of-binary-tree/description/\n */\nfunction minDepth(root: TreeNode | null): number {\n let stack: Array<TreeNode | null> = [],\n depth = 0\n if (!root) return depth\n stack.push(root)\n\n while (stack.length) {\n let tempStack = stack.concat()\n let flag = false\n for (let i = 0; i < tempStack.length; i++) {\n let node = stack.shift()\n\n if (!node?.left && !node?.right) {\n flag = true\n }\n\n node?.left && stack.push(node.left)\n node?.right && stack.push(node.right)\n }\n depth += 1\n\n if (flag) {\n return depth\n }\n }\n return depth\n}\n",
"url": "https://leetcode.cn/problems/binary-tree-level-order-traversal/description/",
"sortKey": 3
},
{
"name": "4.翻转二叉树.ts",
"type": "file",
"path": "src/6.二叉树/4.翻转二叉树.ts",
"content": "/**\n * @url https://leetcode.cn/problems/invert-binary-tree/description/\n */\n\nimport { rootCertificates } from \"tls\"\n\nclass TreeNode {\n val: number\n left: TreeNode | null\n right: TreeNode | null\n constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {\n this.val = val === undefined ? 0 : val\n this.left = left === undefined ? null : left\n this.right = right === undefined ? null : right\n }\n}\n\nfunction invertTree(root: TreeNode | null): TreeNode | null {\n if (!root) return null\n if (root) {\n let p = root.left\n root.left = root.right\n root.right = p\n invertTree(root.left)\n invertTree(root.right)\n }\n return root\n}\n",
"url": "https://leetcode.cn/problems/invert-binary-tree/description/",
"sortKey": 4
},
{
"name": "5.对称二叉树.ts",
"type": "file",
"path": "src/6.二叉树/5.对称二叉树.ts",
"content": "/**\n * @url https://leetcode.cn/problems/symmetric-tree/description/\n */\n\nclass TreeNode {\n val: number\n left: TreeNode | null\n right: TreeNode | null\n constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {\n this.val = val === undefined ? 0 : val\n this.left = left === undefined ? null : left\n this.right = right === undefined ? null : right\n }\n}\n\n// notice:compare比较单个单点的函数,然后比较左子树,右子树的节点\n\nfunction isSymmetric(root: TreeNode | null): boolean {\n const compare = (left: TreeNode | null, right: TreeNode | null) => {\n const specialCondition = (left && !right) || (!left && right)\n const empty = !left && !right\n if (specialCondition) {\n return false\n }\n if (empty) {\n return true\n }\n if (left?.val !== right?.val) {\n return false\n }\n const inSide = compare(left!.left, right!.right)\n const outSide = compare(left!.right, right!.left)\n return inSide && outSide\n }\n if (!root) return true\n return compare(root.left, root.right)\n}\n\n/**\n * @description 相同的树\n * @url https://leetcode.cn/problems/same-tree/\n */\nfunction isSameTree(p: TreeNode | null, q: TreeNode | null): boolean {\n const compare = (p: TreeNode | null, q: TreeNode | null) => {\n // 比较单节点\n const special = (!p && q) || (p && !q)\n if (special) return false\n const isEmpty = !p && !q\n if (isEmpty) return true\n if (p?.val !== q?.val) return false\n // 比较子树\n return compare(p!.left, q!.left) && compare(p!.right, q!.right)\n }\n return compare(p, q)\n}\n\n/**\n * @description 另一个树的子树\n * @url https://leetcode.cn/problems/subtree-of-another-tree/description/\n */\n\n// compare比较根节点相同的两个子树是否相同\nconst compare = (root: TreeNode | null, subRoot: TreeNode | null) => {\n const specialCondition = (root && !subRoot) || (!root && subRoot)\n const empty = !root && !subRoot\n if (specialCondition) {\n return false\n }\n if (empty) {\n return true\n }\n if (root?.val !== subRoot?.val) {\n return false\n }\n return compare(root!.left, subRoot!.left) && compare(root!.right, subRoot!.right)\n}\nfunction isSubtree(root: TreeNode | null, subRoot: TreeNode | null): boolean {\n // 对root进行dfs的遍历\n if (!root) return false\n return compare(root, subRoot) || isSubtree(root.left, subRoot) || isSubtree(root.right, subRoot)\n}\n",
"url": "https://leetcode.cn/problems/symmetric-tree/description/",
"sortKey": 5
},
{
"name": "6.二叉树的最大深度.ts",
"type": "file",
"path": "src/6.二叉树/6.二叉树的最大深度.ts",
"content": "/**\n * @description 递归法\n * @url https://leetcode.cn/problems/maximum-depth-of-binary-tree/description/\n */\n\nclass TreeNode {\n val: number\n left: TreeNode | null\n right: TreeNode | null\n constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {\n this.val = val === undefined ? 0 : val\n this.left = left === undefined ? null : left\n this.right = right === undefined ? null : right\n }\n}\n\n// 自上而下求深度\nfunction maxDepth(root: TreeNode | null): number {\n const dfs = (root, depth) => {\n if (!root) return depth\n return Math.max(dfs(root.left, depth + 1), dfs(root.right, depth + 1))\n }\n return dfs(root, 0)\n}\n// 自下而上求高度\nfunction maxDepthDeep(root: TreeNode | null): number {\n const dfs = (root) => {\n if (!root) return 0\n return 1 + Math.max(dfs(root.left), dfs(root.right))\n }\n return dfs(root)\n}\n",
"url": "https://leetcode.cn/problems/maximum-depth-of-binary-tree/description/",
"sortKey": 6
},
{
"name": "7.二叉树的最小深度.ts",
"type": "file",
"path": "src/6.二叉树/7.二叉树的最小深度.ts",
"content": "/**\n * @url https://leetcode.cn/problems/minimum-depth-of-binary-tree/description/\n */\n\n// TODO:有特殊case,单链表类型的特殊case\nclass TreeNode {\n val: number\n left: TreeNode | null\n right: TreeNode | null\n constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {\n this.val = val === undefined ? 0 : val\n this.left = left === undefined ? null : left\n this.right = right === undefined ? null : right\n }\n}\n\n// 通过计算高度来算的\nfunction minDepth(root: TreeNode | null): number {\n const dfs = (root: TreeNode | null) => {\n if (!root) return 0\n if (!root.left && root.right) {\n return 1 + dfs(root.right)\n }\n if (root.left && !root.right) {\n return 1 + dfs(root.left)\n }\n return 1 + Math.min(dfs(root.left), dfs(root.right))\n }\n return dfs(root)\n}\n\n// 通过深度来计算\nfunction minDepthTwo(root: TreeNode | null): number {\n const dfs = (root: TreeNode | null, depth) => {\n if (!root) return 0\n if (!root.left && root.right) {\n return dfs(root.right, depth + 1)\n }\n if (root.left && !root.right) {\n return dfs(root.left, depth + 1)\n }\n return Math.min(dfs(root.left, depth + 1), dfs(root.right, depth + 1))\n }\n return dfs(root, 0)\n}\n",
"url": "https://leetcode.cn/problems/minimum-depth-of-binary-tree/description/",
"sortKey": 7
},
{
"name": "8.完全二叉树的节点个数.ts",
"type": "file",
"path": "src/6.二叉树/8.完全二叉树的节点个数.ts",
"content": "/**\n * @url https://leetcode.cn/problems/count-complete-tree-nodes/description/\n * @videoUrl https://www.bilibili.com/video/BV1eW4y1B7pD/?spm_id_from=333.788&vd_source=1c79b7395f5d242a2f6786026aac6213\n */\n\nclass TreeNode {\n val: number\n left: TreeNode | null\n right: TreeNode | null\n constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {\n this.val = val === undefined ? 0 : val\n this.left = left === undefined ? null : left\n this.right = right === undefined ? null : right\n }\n}\n\n// TODO:完全二叉树和满二叉树的概念\n\nfunction countNodes(root: TreeNode | null): number {\n if (!root) {\n // console.log(leftDepth, rightDepth)\n return 0\n }\n\n let left = root.left,\n right = root.right,\n leftDepth = 1,\n rightDepth = 1\n while (left) {\n left = left.left\n leftDepth++\n }\n while (right) {\n right = right.right\n rightDepth++\n }\n if (leftDepth === rightDepth) {\n if (leftDepth !== 1) {\n return 2 ** leftDepth - 1\n }\n return 1\n }\n\n return 1 + countNodes(root.left) + countNodes(root.right)\n}\n\n// let root = new TreeNode(1)\n// console.log(countNodes(root))\n",
"url": "https://leetcode.cn/problems/count-complete-tree-nodes/description/",
"sortKey": 8
},
{
"name": "9.平衡二叉树.ts",
"type": "file",
"path": "src/6.二叉树/9.平衡二叉树.ts",
"content": "/**\n * @url https://leetcode.cn/problems/balanced-binary-tree/description/\n * @videoUrl https://www.bilibili.com/video/BV1Ug411S7my/?spm_id_from=333.788&vd_source=1c79b7395f5d242a2f6786026aac6213\n */\n\n// TODO:准备二刷了(高度和深度的理解)\n// 这题只能求高度\nfunction isBalanced(root: TreeNode | null): boolean {\n const dfs = (root: TreeNode | null) => {\n if (!root) return 0\n // notice:单层递归的逻辑\n let leftHeight = dfs(root.left)\n if (leftHeight === -1) {\n return -1\n }\n let rightHeight = dfs(root.right)\n if (rightHeight === -1) {\n return -1\n }\n return Math.abs(leftHeight - rightHeight) > 1 ? -1 : Math.max(leftHeight, rightHeight) + 1\n }\n if (!root) return true\n return dfs(root) !== -1\n}\n",
"url": "https://leetcode.cn/problems/balanced-binary-tree/description/",
"sortKey": 9
},
{
"name": "10.二叉树的所有路径.ts",
"type": "file",
"path": "src/6.二叉树/10.二叉树的所有路径.ts",
"content": "/**\n * @url https://leetcode.cn/problems/binary-tree-paths/description/\n */\n\nclass TreeNode {\n val: number\n left: TreeNode | null\n right: TreeNode | null\n constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {\n this.val = val === undefined ? 0 : val\n this.left = left === undefined ? null : left\n this.right = right === undefined ? null : right\n }\n}\nfunction binaryTreePaths(root: TreeNode | null): string[] {\n let path: Array<string> = []\n const dfs = (root: TreeNode | null | undefined, pathTempArray: Array<string>) => {\n if (!root) return\n pathTempArray.push(String(root?.val))\n\n if (!root?.left && !root?.right) {\n path.push(pathTempArray.join(\"->\"))\n return\n }\n dfs(root?.left, pathTempArray.concat())\n dfs(root?.right, pathTempArray.concat())\n }\n dfs(root, [])\n return path\n}\n",
"url": "https://leetcode.cn/problems/binary-tree-paths/description/",
"sortKey": 10
},
{
"name": "11.左叶子之和.ts",
"type": "file",
"path": "src/6.二叉树/11.左叶子之和.ts",
"content": "/**\n * @url https://leetcode.cn/problems/sum-of-left-leaves/description/\n */\n\nclass TreeNode {\n val: number;\n left: TreeNode | null;\n right: TreeNode | null;\n constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {\n this.val = val === undefined ? 0 : val;\n this.left = left === undefined ? null : left;\n this.right = right === undefined ? null : right;\n }\n}\n\nfunction sumOfLeftLeaves(root: TreeNode | null): number {\n let sum = 0;\n const dfs = (root: TreeNode | null | undefined) => {\n if (!root) return;\n if (root.left && !root.left.left && !root.left.right) {\n sum += root.left.val;\n // return\n // PS:notice:这里容易return掉\n }\n dfs(root.left);\n dfs(root.right);\n };\n dfs(root);\n return sum;\n}\n\n// ;[3, 9, 20, null, null, 15, 7]\n\nlet root = new TreeNode(\n 3,\n new TreeNode(9),\n new TreeNode(20, new TreeNode(15), new TreeNode(7))\n);\nconsole.log(sumOfLeftLeaves(root));\n",
"url": "https://leetcode.cn/problems/sum-of-left-leaves/description/",
"sortKey": 11
},
{
"name": "12.找树左下角的值.ts",
"type": "file",
"path": "src/6.二叉树/12.找树左下角的值.ts",
"content": "/**\n * @url https://leetcode.cn/problems/find-bottom-left-tree-value/description/\n */\n\nclass TreeNode {\n val: number;\n left: TreeNode | null;\n right: TreeNode | null;\n constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {\n this.val = val === undefined ? 0 : val;\n this.left = left === undefined ? null : left;\n this.right = right === undefined ? null : right;\n }\n}\n\nfunction findBottomLeftValue(root: TreeNode | null): number {\n let maxDepth = Number.MIN_SAFE_INTEGER,\n res;\n const dfs = (root: TreeNode | null | undefined, depth) => {\n if (!root) return -1;\n if (!root.left && !root.right) {\n if (maxDepth < depth) {\n maxDepth = depth;\n res = root.val;\n return;\n }\n }\n // 找最左下角的值,所以先遍历左边,然后先判断下手为强\n dfs(root.left, depth + 1);\n dfs(root.right, depth + 1);\n };\n dfs(root, 1);\n return res;\n}\n\n// 层序遍历去做,不过会超时\n// function findBottomLeftValue(root: TreeNode | null): number {\n// let val = 0;\n// const stack = []\n// stack.push(root)\n// while(stack.length){\n// let copyStack = stack.concat()\n// console.log(copyStack)\n// for(let i = 0; i < copyStack.length; i++){\n// const top = stack.shift()\n// if(i === 0){\n// val = top.val\n// }\n// if(top?.left){\n// stack.push(top.left)\n// }\n// if(top?.right){\n// stack.push(top.right)\n// }\n// }\n// }\n// return val\n// };\n",
"url": "https://leetcode.cn/problems/find-bottom-left-tree-value/description/",
"sortKey": 12
},
{
"name": "13.路径总和.ts",
"type": "file",
"path": "src/6.二叉树/13.路径总和.ts",
"content": "/**\n * @url https://leetcode.cn/problems/path-sum/description/\n */\n\nclass TreeNode {\n val: number\n left: TreeNode | null\n right: TreeNode | null\n constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {\n this.val = val === undefined ? 0 : val\n this.left = left === undefined ? null : left\n this.right = right === undefined ? null : right\n }\n}\nfunction hasPathSum(root: TreeNode | null, targetSum: number): boolean {\n const dfs = (root: TreeNode | null, sum) => {\n if (!root) {\n // 这里返回什么都无所谓,主要是return 掉\n return 0\n }\n if (!root.left && !root.right) {\n sum += root.val\n if (sum === targetSum) {\n return true\n } else {\n return false\n }\n }\n\n return dfs(root.left, sum + root.val) || dfs(root.right, sum + root.val)\n }\n return dfs(root, 0)\n}\n",
"url": "https://leetcode.cn/problems/path-sum/description/",
"sortKey": 13
},
{
"name": "14.从中序与后序遍历序列构造二叉树.ts",
"type": "file",
"path": "src/6.二叉树/14.从中序与后序遍历序列构造二叉树.ts",
"content": "/**\n * @url https://leetcode.cn/problems/construct-binary-tree-from-inorder-and-postorder-traversal/description/\n * @videoUrl https://www.bilibili.com/video/BV1vW4y1i7dn/?vd_source=1c79b7395f5d242a2f6786026aac6213\n */\n\nclass TreeNode {\n val: number\n left: TreeNode | null\n right: TreeNode | null\n constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {\n this.val = val === undefined ? 0 : val\n this.left = left === undefined ? null : left\n this.right = right === undefined ? null : right\n }\n}\nfunction buildTree(inorder: number[], postorder: number[]): TreeNode | null {\n if (postorder.length === 0) return null\n let midValue = postorder.pop()\n const root = new TreeNode(midValue)\n let _index = inorder.indexOf(midValue!)\n root.left = buildTree(inorder.slice(0, _index), postorder.slice(0, _index)) // notice:注意中间节点需要去掉\n root.right = buildTree(inorder.slice(_index + 1), postorder.slice(_index))\n return root\n}\n\n/**\n * @description 从前序与中序遍历序列构造二叉树\n * @url https://leetcode.cn/problems/construct-binary-tree-from-preorder-and-inorder-traversal/description/\n */\nfunction buildTree(preorder: number[], inorder: number[]): TreeNode | null {\n if (preorder.length === 0) return null\n let _mid = preorder.shift() // notice:一定要注意!!!真的闹糊涂了\n let _root = new TreeNode(_mid)\n let _index = inorder.indexOf(_mid!)\n _root.left = buildTree(preorder.slice(0, _index), inorder.slice(0, _index))\n _root.right = buildTree(preorder.slice(_index), inorder.slice(_index + 1))\n return _root\n}\n",
"url": "https://leetcode.cn/problems/construct-binary-tree-from-inorder-and-postorder-traversal/description/",
"sortKey": 14
},
{
"name": "15.最大二叉树.ts",
"type": "file",
"path": "src/6.二叉树/15.最大二叉树.ts",
"content": "/**\n * @url https://leetcode.cn/problems/maximum-binary-tree/description/\n */\n\nclass TreeNode {\n val: number\n left: TreeNode | null\n right: TreeNode | null\n constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {\n this.val = val === undefined ? 0 : val\n this.left = left === undefined ? null : left\n this.right = right === undefined ? null : right\n }\n}\n\nfunction constructMaximumBinaryTree(nums: number[]): TreeNode | null {\n if (nums.length === 0) return null\n if (nums.length === 1) return new TreeNode(nums[0])\n let _maxIdx = nums.indexOf(Math.max(...nums))\n let _root = new TreeNode(nums[_maxIdx])\n _root.left = constructMaximumBinaryTree(nums.slice(0, _maxIdx))\n _root.right = constructMaximumBinaryTree(nums.slice(_maxIdx + 1))\n return _root\n}\n",
"url": "https://leetcode.cn/problems/maximum-binary-tree/description/",
"sortKey": 15
},
{
"name": "16.合并二叉树.ts",
"type": "file",
"path": "src/6.二叉树/16.合并二叉树.ts",
"content": "/**\n * @url https://leetcode.cn/problems/merge-two-binary-trees/description/\n */\n\nclass TreeNode {\n val: number\n left: TreeNode | null\n right: TreeNode | null\n constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {\n this.val = val === undefined ? 0 : val\n this.left = left === undefined ? null : left\n this.right = right === undefined ? null : right\n }\n}\nfunction mergeTrees(root1: TreeNode | null, root2: TreeNode | null): TreeNode | null {\n if (!root1 && !root2) {\n return null\n }\n if (!root1) return root2\n if (!root2) return root1\n root1.left = mergeTrees(root1.left, root2.left)\n root1.right = mergeTrees(root1.right, root2.right)\n root1.val += root2.val\n\n return root1\n}\n",
"url": "https://leetcode.cn/problems/merge-two-binary-trees/description/",
"sortKey": 16
},
{
"name": "17.二叉搜索树的搜索.ts",
"type": "file",
"path": "src/6.二叉树/17.二叉搜索树的搜索.ts",
"content": "/**\n * @url https://leetcode.cn/problems/search-in-a-binary-search-tree/\n */\n\nclass TreeNode {\n val: number\n left: TreeNode | null\n right: TreeNode | null\n constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {\n this.val = val === undefined ? 0 : val\n this.left = left === undefined ? null : left\n this.right = right === undefined ? null : right\n }\n}\n// TODO:注意二叉搜索树的特性\n/** 暴力法 */\nfunction searchBST(root: TreeNode | null, val: number): TreeNode | null {\n const dfs = (root: TreeNode | null | undefined) => {\n if (!root) return null\n\n if (root.left) {\n const res = dfs(root.left)\n if (res) {\n return res\n }\n }\n\n if (root.val === val) {\n return root\n }\n if (root.right) {\n const res = dfs(root.right)\n if (res) {\n return res\n }\n }\n return null\n }\n return dfs(root)\n}\n\n/** 利用二叉搜索树的特性法 */\nfunction searchBSTDeep(root: TreeNode | null, val: number): TreeNode | null {\n const dfs = (root: TreeNode | null | undefined) => {\n if (!root) return null\n\n if (root.val === val) {\n return root\n } else if (root.val < val && root.right) {\n const res = dfs(root.right)\n if (res) {\n return res\n }\n } else {\n if (root.left) {\n const res = dfs(root.left)\n if (res) {\n return res\n }\n }\n }\n\n return null\n }\n return dfs(root)\n}\n",
"url": "https://leetcode.cn/problems/search-in-a-binary-search-tree/",
"sortKey": 17
},
{
"name": "18.验证二叉搜索树.ts",
"type": "file",
"path": "src/6.二叉树/18.验证二叉搜索树.ts",
"content": "/**\n * @url https://leetcode.cn/problems/validate-binary-search-tree/\n */\n\nclass TreeNode {\n val: number\n left: TreeNode | null\n right: TreeNode | null\n constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {\n this.val = val === undefined ? 0 : val\n this.left = left === undefined ? null : left\n this.right = right === undefined ? null : right\n }\n}\n\n// TODO:细节挺多的\nfunction isValidBST(root: TreeNode | null): boolean {\n let last: TreeNode\n const dfs = (root: TreeNode | null | undefined) => {\n if (!root) return true\n const left = dfs(root.left)\n if (last && root.val <= last.val) return false\n last = root\n const right = dfs(root.right)\n return left && right\n }\n return dfs(root)\n}\n",
"url": "https://leetcode.cn/problems/validate-binary-search-tree/",
"sortKey": 18
},
{
"name": "19.二叉搜索树的最小绝对值差.ts",
"type": "file",
"path": "src/6.二叉树/19.二叉搜索树的最小绝对值差.ts",
"content": "/**\n * @url https://leetcode.cn/problems/minimum-absolute-difference-in-bst/description/\n */\n\nclass TreeNode {\n val: number\n left: TreeNode | null\n right: TreeNode | null\n constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {\n this.val = val === undefined ? 0 : val\n this.left = left === undefined ? null : left\n this.right = right === undefined ? null : right\n }\n}\n\nfunction getMinimumDifference(root: TreeNode | null): number {\n let pre: TreeNode,\n res = Number.MAX_SAFE_INTEGER\n const dfs = (root: TreeNode | null | undefined) => {\n if (!root) return Number.MAX_SAFE_INTEGER\n dfs(root.left)\n if (pre) {\n res = Math.min(Math.abs(root.val - pre.val), res)\n }\n pre = root\n dfs(root.right)\n return res\n }\n return dfs(root)\n}\n",
"url": "https://leetcode.cn/problems/minimum-absolute-difference-in-bst/description/",
"sortKey": 19
},
{
"name": "20.二叉搜索树的众数.ts",
"type": "file",
"path": "src/6.二叉树/20.二叉搜索树的众数.ts",
"content": "/**\n * @url https://leetcode.cn/problems/find-mode-in-binary-search-tree/description/\n */\n\nclass TreeNode {\n val: number\n left: TreeNode | null\n right: TreeNode | null\n constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {\n this.val = val === undefined ? 0 : val\n this.left = left === undefined ? null : left\n this.right = right === undefined ? null : right\n }\n}\n\n// TODO:有坑\nfunction findMode(root: TreeNode | null): number[] {\n let res: number[] = [],\n maxCount = Number.MIN_SAFE_INTEGER,\n prev: TreeNode | null | undefined,\n curCount = 0\n\n const dfs = (root: TreeNode | null | undefined) => {\n if (!root) return\n dfs(root.left)\n if (prev && root.val === prev.val) {\n curCount++\n if (curCount === maxCount) {\n res.push(root.val)\n } else if (curCount > maxCount) {\n res = []\n res.push(root.val)\n maxCount = curCount\n }\n } else {\n curCount = 1\n }\n if (!prev) {\n res.push(root.val)\n }\n prev = root\n\n dfs(root.right)\n }\n dfs(root)\n return res\n}\n\n// 构造二叉树\nfunction buildTree(preorder: number[], inorder: number[]): TreeNode | null {\n let _mid = preorder.shift() // notice:一定要注意!!!真的闹糊涂了\n if (preorder.length === 0) return null\n let _root = new TreeNode(_mid)\n let _index = inorder.indexOf(_mid!)\n _root.left = buildTree(preorder.slice(0, _index), inorder.slice(0, _index))\n _root.right = buildTree(preorder.slice(_index), inorder.slice(_index + 1))\n return _root\n}\n\nconst root = buildTree([1, 2], [1, 2])\nconsole.log(root)\n",
"url": "https://leetcode.cn/problems/find-mode-in-binary-search-tree/description/",
"sortKey": 20
},
{
"name": "21.二叉树的最近公共祖先.ts",
"type": "file",
"path": "src/6.二叉树/21.二叉树的最近公共祖先.ts",
"content": "/**\n * @url https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-tree/description/\n */\n\nclass TreeNode {\n val: number\n left: TreeNode | null\n right: TreeNode | null\n constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {\n this.val = val === undefined ? 0 : val\n this.left = left === undefined ? null : left\n this.right = right === undefined ? null : right\n }\n}\n\n// notice:后序遍历将结果返回上去,最后返回最终结果!\nfunction lowestCommonAncestor(root: TreeNode | null, p: TreeNode | null, q: TreeNode | null): TreeNode | null {\n if (!root) return null\n if (root === q || root === p) return root\n const left = lowestCommonAncestor(root.left, p, q)\n const right = lowestCommonAncestor(root.right, p, q)\n if (left && !right) {\n return left\n }\n if (!left && right) {\n return right\n }\n if (left && right) {\n return root\n }\n return null\n}\n",
"url": "https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-tree/description/",
"sortKey": 21
},
{
"name": "22.二叉搜索树的最近公共祖先.ts",
"type": "file",
"path": "src/6.二叉树/22.二叉搜索树的最近公共祖先.ts",
"content": "/**\n * @url https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-search-tree/\n */\n\nclass TreeNode {\n val: number\n left: TreeNode | null\n right: TreeNode | null\n constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {\n this.val = val === undefined ? 0 : val\n this.left = left === undefined ? null : left\n this.right = right === undefined ? null : right\n }\n}\nfunction lowestCommonAncestor(root: TreeNode | null, p: TreeNode | null, q: TreeNode | null): TreeNode | null {\n if (!root) return null\n if (root === q || root === p) return root\n\n if (root.val > p!.val && root.val > q!.val) {\n const left = lowestCommonAncestor(root.left, p, q)\n if (left) {\n return left\n }\n }\n if (root.val < p!.val && root.val < q!.val) {\n const right = lowestCommonAncestor(root.right, p, q)\n if (right) {\n return right\n }\n }\n const left = lowestCommonAncestor(root.left, p, q)\n const right = lowestCommonAncestor(root.right, p, q)\n if (!left && right) return right\n if (left && !right) return left\n if (left && right) return root\n return null\n}\n",
"url": "https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-search-tree/",
"sortKey": 22
},
{
"name": "23.二叉搜索树的插入操作.ts",
"type": "file",
"path": "src/6.二叉树/23.二叉搜索树的插入操作.ts",
"content": "/**\n * @url https://leetcode.cn/problems/insert-into-a-binary-search-tree/description/\n */\n\nclass TreeNode {\n val: number;\n left: TreeNode | null;\n right: TreeNode | null;\n constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {\n this.val = val === undefined ? 0 : val;\n this.left = left === undefined ? null : left;\n this.right = right === undefined ? null : right;\n }\n}\n// function insertIntoBST(root: TreeNode | null, val: number): TreeNode | null {\n// const dfs = (root: TreeNode | null, val: number) => {\n// if (!root) {\n// return\n// }\n// if (!root.left && !root.right) {\n// // TODO:这样没有返回值的算法,会丢失掉左子树全为空的情况\n// if (root.val > val) {\n// root.left = new TreeNode(val)\n// } else {\n// root.right = new TreeNode(val)\n// }\n// return null\n// }\n// if (root.val > val) {\n// dfs(root.left, val)\n// }\n// if (root.val < val) {\n// dfs(root.right, val)\n// }\n// }\n// if (!root) return new TreeNode(val)\n// dfs(root, val)\n// return root\n// }\n/**\n * Definition for a binary tree node.\n * function TreeNode(val, left, right) {\n * this.val = (val===undefined ? 0 : val)\n * this.left = (left===undefined ? null : left)\n * this.right = (right===undefined ? null : right)\n * }\n */\n\n// //========\n/**\n * @param {TreeNode} root\n * @param {number} val\n * @return {TreeNode}\n */\nvar insertIntoBST = function (root, val) {\n const dfs = (root) => {\n if (!root) return new TreeNode(val);\n if (root.val > val) {\n const left = dfs(root.left);\n root.left = left;\n } else {\n const right = dfs(root.right);\n root.right = right;\n }\n return root;\n };\n return dfs(root);\n};\n",
"url": "https://leetcode.cn/problems/insert-into-a-binary-search-tree/description/",
"sortKey": 23
},
{
"name": "24.删除二叉搜索树的节点.ts",
"type": "file",
"path": "src/6.二叉树/24.删除二叉搜索树的节点.ts",
"content": "/**\n * @url https://leetcode.cn/problems/delete-node-in-a-bst/description/\n */\n\nclass TreeNode {\n val: number;\n left: TreeNode | null;\n right: TreeNode | null;\n constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {\n this.val = val === undefined ? 0 : val;\n this.left = left === undefined ? null : left;\n this.right = right === undefined ? null : right;\n }\n}\n\n// notice\n// 总共有五种情况\n// 叶子节点,直接删掉就可以\n// 没有找到节点\n// 删除的节点只有左孩子,没有右孩子\n// 删除的节点只有右孩子,没有左孩子\n// 删除的节点既有右孩子,也有左孩子\nfunction deleteNode(root: TreeNode | null, key: number): TreeNode | null {\n if (!root) return null;\n if (root.val === key) {\n if (!root.left && !root.right) {\n return null;\n }\n if (root.left && !root.right) {\n return root.left;\n }\n if (!root.left && root.right) {\n return root.right;\n }\n if (root.left && root.right) {\n let cur = root.right;\n while (cur.left) {\n cur = cur.left;\n }\n cur.left = root.left;\n return root.right;\n }\n }\n\n if (root.val > key) {\n root.left = deleteNode(root.left, key);\n } else {\n root.right = deleteNode(root.right, key);\n }\n return root;\n}\n",
"url": "https://leetcode.cn/problems/delete-node-in-a-bst/description/",
"sortKey": 24
},
{
"name": "25.修剪二叉搜素树.ts",
"type": "file",
"path": "src/6.二叉树/25.修剪二叉搜素树.ts",
"content": "/**\n * @url https://leetcode.cn/problems/trim-a-binary-search-tree/description/\n */\n\nclass TreeNode {\n val: number;\n left: TreeNode | null;\n right: TreeNode | null;\n constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {\n this.val = val === undefined ? 0 : val;\n this.left = left === undefined ? null : left;\n this.right = right === undefined ? null : right;\n }\n}\n// TODO: 如果直接返回左右子树的话,没有考虑左右子树里面还有不符合条件的子树\n/**\n * 沿用上一题的思路稍微改一下就可以了。上一题只需要删除一个结点,然后直接return,导致其下面还符合条件的结点没法删除。原因是因为上一题实\n 际上是个先序遍历,符合条件直接return,如果我们改为后序遍历就可以了,每次判定都会是最底下的结点先判定,也就是把判断的代码写到递归代码 \n 的后面。\n */\nfunction trimBST(\n root: TreeNode | null,\n low: number,\n high: number\n): TreeNode | null {\n if (!root) return null;\n\n // 先处理子树里面的子树,这里的遍历顺序有一定的讲究。\n root.left = trimBST(root.left, low, high); // 需要先将左子树进行修剪。\n root.right = trimBST(root.right, low, high); // 需要将右子树进行修剪。\n\n if (root.val < low || root.val > high) {\n if (!root.left && !root.right) {\n return null;\n }\n if (root.left && !root.right) {\n return root.left;\n }\n if (!root.left && root.right) {\n return root.right;\n }\n if (root.left && root.right) {\n let cur = root.right;\n while (cur.left) {\n cur = cur.left;\n }\n cur.left = root.left;\n return root.right;\n }\n }\n return root;\n}\n",
"url": "https://leetcode.cn/problems/trim-a-binary-search-tree/description/",
"sortKey": 25
},
{
"name": "26.将有序数组转换为二叉搜索树.ts",
"type": "file",
"path": "src/6.二叉树/26.将有序数组转换为二叉搜索树.ts",
"content": "/**\n * @url https://leetcode.cn/problems/convert-sorted-array-to-binary-search-tree/description/\n */\n\nclass TreeNode {\n val: number;\n left: TreeNode | null;\n right: TreeNode | null;\n constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {\n this.val = val === undefined ? 0 : val;\n this.left = left === undefined ? null : left;\n this.right = right === undefined ? null : right;\n }\n}\n\nfunction sortedArrayToBST(nums: number[]): TreeNode | null {\n if (nums.length === 0) return null;\n let mid = Math.floor(nums.length / 2);\n let root = new TreeNode(nums[mid]);\n root.left = sortedArrayToBST(nums.slice(0, mid));\n root.right = sortedArrayToBST(nums.slice(mid + 1));\n return root;\n}\n",
"url": "https://leetcode.cn/problems/convert-sorted-array-to-binary-search-tree/description/",
"sortKey": 26
},
{
"name": "27.把二叉搜索树转换为累加树.ts",
"type": "file",
"path": "src/6.二叉树/27.把二叉搜索树转换为累加树.ts",
"content": "/**\n * @url https://leetcode.cn/problems/convert-bst-to-greater-tree/description/\n */\n\nclass TreeNode {\n val: number;\n left: TreeNode | null;\n right: TreeNode | null;\n constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {\n this.val = val === undefined ? 0 : val;\n this.left = left === undefined ? null : left;\n this.right = right === undefined ? null : right;\n }\n}\n\nfunction sortedArrayToBST(nums: number[]): TreeNode | null {\n if (nums.length === 0) return null;\n let mid = Math.floor(nums.length / 2);\n let root = new TreeNode(nums[mid]);\n root.left = sortedArrayToBST(nums.slice(0, mid));\n root.right = sortedArrayToBST(nums.slice(mid + 1));\n return root;\n}\n\n// TODO:一种是维护全局的一个sum。代表累加的值 一种是用prev指针来指向上一个遍历的指针。\nfunction convertBST(root: TreeNode | null): TreeNode | null {\n let sum = 0;\n const dfs = (root: TreeNode | null) => {\n if (!root) return 0;\n dfs(root.right);\n sum += root.val;\n root.val = sum;\n dfs(root.left);\n };\n dfs(root);\n return root;\n}\n\nconst rootArray = [1, 2, 3, 4, 5, 6, 7, 8];\nconst root = sortedArrayToBST(rootArray);\nconsole.log(convertBST(root));\n",
"url": "https://leetcode.cn/problems/convert-bst-to-greater-tree/description/",
"sortKey": 27
}
],
"sortKey": 6
},
{
"name": "7.回溯算法",
"type": "directory",
"path": "src/7.回溯算法",
"children": [
{
"name": "1.组合.ts",
"type": "file",
"path": "src/7.回溯算法/1.组合.ts",
"content": "/**\n * @url https://leetcode.cn/problems/combinations/description/\n */\n\n// TODO:后期关于回溯的题目,统一看看是否能够剪枝。\nfunction combine(n: number, k: number): number[][] {\n const result: Array<Array<number>> = []\n const traceBacking = (idx: number, acc: number[]) => {\n if (acc.length === k) {\n result.push(acc)\n return\n }\n\n for (let i = idx; i <= n; i++) {\n acc.push(i)\n traceBacking(i + 1, acc.concat([]))\n acc.pop()\n }\n }\n traceBacking(1, [])\n return result\n}\n",
"url": "https://leetcode.cn/problems/combinations/description/",
"sortKey": 1
},
{
"name": "2.组合总和三.ts",
"type": "file",
"path": "src/7.回溯算法/2.组合总和三.ts",
"content": "/**\n * @url https://leetcode.cn/problems/combination-sum-iii/description/\n */\n\nfunction combinationSum3(k: number, n: number): number[][] {\n const result: number[][] = []\n const dfs = (idx: number, path: number[]) => {\n if (path.length === k) {\n if (path.reduce((acc, cur) => acc + cur, 0) === n) {\n result.push(path)\n }\n return\n }\n for (let i = idx + 1; i <= 9; i++) {\n path.push(i)\n dfs(i, path.concat([]))\n path.pop()\n }\n }\n dfs(0, [])\n return result\n}\n",
"url": "https://leetcode.cn/problems/combination-sum-iii/description/",
"sortKey": 2
},
{
"name": "3.电话号码的字母组合.ts",
"type": "file",
"path": "src/7.回溯算法/3.电话号码的字母组合.ts",
"content": "/**\n * @url https://leetcode.cn/problems/letter-combinations-of-a-phone-number/description/\n */\n\nfunction init(): Map<string, string> {\n let map = new Map()\n map.set(\"2\", \"abc\")\n map.set(\"3\", \"def\")\n map.set(\"4\", \"ghi\")\n map.set(\"5\", \"jkl\")\n map.set(\"6\", \"mno\")\n map.set(\"7\", \"pqrs\")\n map.set(\"8\", \"tuv\")\n map.set(\"9\", \"wxyz\")\n return map\n}\nfunction letterCombinations(digits: string): string[] {\n const result: string[] = []\n const map = init()\n const dfs = (path: string[], idx: number) => {\n if (path.length === digits.length) {\n result.push(path.join(\"\"))\n return\n }\n const str = map.get(digits[idx])\n for (let i = 0; i < str!.length; i++) {\n path.push(str![i])\n dfs(path.concat([]), idx + 1)\n path.pop()\n }\n }\n dfs([], 0)\n return result.filter((_str) => _str !== \"\")\n}\n// console.log(letterCombinations(\"23\"))\n",
"url": "https://leetcode.cn/problems/letter-combinations-of-a-phone-number/description/",
"sortKey": 3
},
{
"name": "4.组合总和.ts",
"type": "file",
"path": "src/7.回溯算法/4.组合总和.ts",
"content": "/**\n * @url https://leetcode.cn/problems/combination-sum/description/\n */\n\n// notice:没有去重操作,用Set来进行去重\n// function combinationSum(candidates: number[], target: number): number[][] {\n// const result: number[][] = []\n// const set = new Set()\n// const dfs = (path: number[]) => {\n// const sum = path.reduce((acc, cur) => acc + cur, 0)\n// if (sum > target) {\n// return\n// }\n// if (sum === target) {\n// set.add(JSON.stringify(path.sort((a, b) => a - b)))\n// return\n// }\n// for (let idx = 0; idx < candidates.length; idx++) {\n// path.push(candidates[idx])\n// dfs(path.concat())\n// path.pop()\n// }\n// }\n// dfs([])\n\n// for (let item of set) {\n// result.push(JSON.parse(item as string))\n// }\n// return result\n// }\n// 去重操作,在traverse的时候进行去重 // ps:利用startIdx来去进行去重\nfunction combinationSum(candidates: number[], target: number): number[][] {\n const res: number[][] = [];\n const dfs = (idx: number, path: number[]) => {\n const sum = path.reduce((acc, cur) => acc + cur, 0);\n if (sum > target) {\n return;\n }\n if (sum === target) {\n res.push(path);\n return;\n }\n for (let i = idx; i < candidates.length; i++) {\n path.push(candidates[i]);\n dfs(i, path.concat([]));\n path.pop();\n }\n };\n dfs(0, []);\n return res;\n}\n",
"url": "https://leetcode.cn/problems/combination-sum/description/",
"sortKey": 4
},
{
"name": "5.组合总和2.ts",
"type": "file",
"path": "src/7.回溯算法/5.组合总和2.ts",
"content": "/**\n * @url https://leetcode.cn/problems/combination-sum-ii/description/\n */\n// PS:难点去重\nfunction combinationSum2(candidates: number[], target: number): number[][] {\n const result: Array<Array<number>> = [];\n const used: Array<boolean> = new Array(candidates.length).fill(false);\n candidates.sort((a, b) => a - b); // ps:这里排序的意义是防止后面有与前面相同的元素\n const dfs = (startIdx, path) => {\n const sum = path.reduce((acc, cur) => acc + cur, 0);\n if (sum === target) {\n result.push(path);\n return;\n }\n if (sum > target) {\n return;\n }\n for (let i = startIdx; i < candidates.length; i++) {\n if (i > 0 && candidates[i] === candidates[i - 1] && !used[i - 1]) {\n continue;\n }\n used[i] = true;\n path.push(candidates[i]);\n dfs(i + 1, path.concat([]));\n path.pop();\n used[i] = false;\n }\n };\n dfs(0, []);\n return result;\n}\n",
"url": "https://leetcode.cn/problems/combination-sum-ii/description/",
"sortKey": 5
},
{
"name": "6.分割回文串.ts",
"type": "file",
"path": "src/7.回溯算法/6.分割回文串.ts",
"content": "/**\n * @url https://leetcode.cn/problems/palindrome-partitioning/description/\n */\n\nconst isBackString = (str: string) => {\n return str === str.split(\"\").reverse().join(\"\")\n}\n\nfunction partition(s: string): string[][] {\n const result: string[][] = []\n const dfs = (startIdx: number, path: string[]) => {\n if (startIdx >= s.length) {\n result.push(path)\n return\n }\n for (let idx = startIdx; idx < s.length; idx++) {\n const str = s.slice(startIdx, idx + 1)\n if (isBackString(str)) {\n path.push(str)\n dfs(idx + 1, path.concat([]))\n path.pop()\n } else {\n continue\n }\n }\n }\n dfs(0, [])\n return result\n}\n",
"url": "https://leetcode.cn/problems/palindrome-partitioning/description/",
"sortKey": 6
},
{
"name": "7.复原ip地址.ts",
"type": "file",
"path": "src/7.回溯算法/7.复原ip地址.ts",
"content": "/**\n * @url https://leetcode.cn/problems/restore-ip-addresses/description/\n */\n\n// 是否是有效字符串\nconst isValidStr = (str: string) => {\n if (str.length > 1) {\n if (str.startsWith(\"0\")) {\n return false\n }\n if (Number(str) >= 0 && Number(str) <= 255) {\n return true\n }\n return false\n }\n return true\n}\nfunction restoreIpAddresses(s: string): string[] {\n const result: string[] = []\n const dfs = (path: string[], startIdx: number) => {\n if (path.length === 4 && path.length === s.length) {\n result.push(path.join(\".\"))\n return\n }\n for (let i = startIdx; i < s.length; i++) {\n const str = s.slice(startIdx, i + 1)\n if (isValidStr(str)) {\n path.push(str)\n dfs(path.concat([]), i + 1)\n path.pop()\n } else {\n continue\n }\n }\n }\n dfs([], 0)\n return result\n}\n",
"url": "https://leetcode.cn/problems/restore-ip-addresses/description/",
"sortKey": 7
},
{
"name": "8.子集.ts",
"type": "file",
"path": "src/7.回溯算法/8.子集.ts",
"content": "/**\n * @url https://leetcode.cn/problems/subsets/description/\n */\nfunction subsets(nums: number[]): number[][] {\n const result: number[][] = []\n const dfs = (path: number[], startIdx: number) => {\n result.push(path)\n if (startIdx >= nums.length) {\n return\n }\n for (let i = startIdx; i < nums.length; i++) {\n path.push(nums[i])\n dfs(path.concat([]), i + 1)\n path.pop()\n }\n }\n dfs([], 0)\n return result\n}\n",
"url": "https://leetcode.cn/problems/subsets/description/",
"sortKey": 8
},
{
"name": "9.子集二.ts",
"type": "file",
"path": "src/7.回溯算法/9.子集二.ts",
"content": "/**\n * @url https://leetcode.cn/problems/subsets-ii/description/\n */\n\n// [1,2,2,3]\nfunction subsetsWithDup(nums: number[]): number[][] {\n const result: number[][] = []\n const used: boolean[] = new Array(nums.length).fill(false)\n nums.sort((a, b) => a - b)\n const dfs = (path: number[], startIdx: number) => {\n result.push(path)\n if (startIdx >= nums.length) {\n return\n }\n for (let i = startIdx; i < nums.length; i++) {\n if (i > 0 && nums[i] === nums[i - 1] && !used[i - 1]) {\n continue\n }\n used[i] = true\n path.push(nums[i])\n dfs(path.concat(), i + 1)\n used[i] = false\n path.pop()\n }\n }\n dfs([], 0)\n return result\n}\n",
"url": "https://leetcode.cn/problems/subsets-ii/description/",
"sortKey": 9
},
{
"name": "10.非递减子序列.ts",
"type": "file",
"path": "src/7.回溯算法/10.非递减子序列.ts",
"content": "/**\n * @url https://leetcode.cn/problems/non-decreasing-subsequences/description/\n */\n\n// TODO:1.边界条件 2.去重逻辑 (不能排序之后用used数组进行去重,会打乱原数组的顺序。)3.用set来去重,在树层的地方判断\n// 想要用used[i]去重,数组就必须要是有序的\n\nfunction findSubsequences(nums: number[]): number[][] {\n const result: Array<Array<number>> = []\n const dfs = (path: number[], startIdx: number) => {\n if (path.length >= 2) {\n result.push(path)\n }\n if (startIdx >= nums.length) {\n return\n }\n const set: Set<number> = new Set()\n for (let i = startIdx; i < nums.length; i++) {\n if (nums[i] < path[path.length - 1]) {\n continue\n }\n if (set.has(nums[i])) {\n continue\n }\n set.add(nums[i])\n path.push(nums[i])\n dfs(path.concat([]), i + 1)\n path.pop()\n }\n }\n dfs([], 0)\n return result\n}\n\nfunction findSubsequences1(nums: number[]): number[][] {\n const res: number[][] = []\n const dfs = (startIdx: number, path: number[]) => {\n if (path.length >= 2) {\n res.push(path)\n }\n\n if (startIdx >= nums.length) {\n return\n }\n const set = new Set()\n\n for (let i = startIdx; i < nums.length; i++) {\n if (nums[i] < path[path.length - 1]) {\n continue\n }\n if (set.has(nums[i])) {\n continue\n }\n path.push(nums[i])\n set.add(nums[i])\n dfs(i + 1, [...path])\n path.pop()\n }\n }\n dfs(0, [])\n return res\n}\n\nconsole.log(findSubsequences1([1, 1, 1, 1, 1, 1]))\n",
"url": "https://leetcode.cn/problems/non-decreasing-subsequences/description/",
"sortKey": 10
},
{
"name": "11.全排列.ts",
"type": "file",
"path": "src/7.回溯算法/11.全排列.ts",
"content": "/**\n * @url https://leetcode.cn/problems/permutations/description/\n */\n// TODO:组合问题是通过startIndex来取下标的,排列问题是通过used数组来取的\nfunction permute(nums: number[]): number[][] {\n const result: number[][] = []\n const dfs = (path: number[], used: boolean[]) => {\n if (path.length === nums.length) {\n result.push(path)\n return\n }\n for (let i = 0; i < nums.length; i++) {\n if (used[i] === true) {\n continue\n }\n path.push(nums[i])\n used[i] = true\n dfs(path.concat([]), used)\n path.pop()\n used[i] = false\n }\n }\n dfs([], new Array(nums.length).fill(false))\n return result\n}\n",
"url": "https://leetcode.cn/problems/permutations/description/",
"sortKey": 11
},
{
"name": "12.全排列二.ts",
"type": "file",
"path": "src/7.回溯算法/12.全排列二.ts",
"content": "/**\n * @url https://leetcode.cn/problems/permutations-ii/description/\n */\n// 跟11比起来就是数层去重\nfunction permuteUnique(nums: number[]): number[][] {\n const result: number[][] = []\n nums.sort((a, b) => a - b)\n const dfs = (path: number[], used: boolean[]) => {\n if (path.length === nums.length) {\n result.push(path)\n return\n }\n\n for (let i = 0; i < nums.length; i++) {\n if (i > 0 && nums[i] === nums[i - 1] && !used[i - 1]) {\n continue\n }\n if (used[i] === true) {\n continue\n }\n path.push(nums[i])\n used[i] = true\n dfs(path.concat([]), used)\n path.pop()\n used[i] = false\n }\n }\n dfs([], new Array(nums.length).fill(false))\n return result\n}\n",
"url": "https://leetcode.cn/problems/permutations-ii/description/",
"sortKey": 12
},
{
"name": "13.N皇后.ts",
"type": "file",
"path": "src/7.回溯算法/13.N皇后.ts",
"content": "/**\n * @url https://leetcode.cn/problems/n-queens/description/\n */\n\nfunction solveNQueens(n: number): string[][] {\n const final: string[][] = []\n const result: string[][] = new Array(n).fill(0).map((_v) => new Array(n).fill(\".\"))\n\n const isValid = (row: number, col: number) => {\n // 检查列上是否有冲突\n for (let i = row; i >= 0; i--) {\n if (result[i][col] === \"Q\") {\n return false\n }\n }\n // 检查右上角上是否有冲突\n for (let i = row, j = col; i >= 0 && j >= 0; j--, i--) {\n if (result[i][j] === \"Q\") {\n return false\n }\n }\n // 检查左上角上是否有冲突\n for (let i = row, j = col; i >= 0 && j < n; i--, j++) {\n if (result[i][j] === \"Q\") {\n return false\n }\n }\n return true\n }\n\n const dfs = (row: number) => {\n if (row === n) {\n final.push(result.concat([]).map((_row) => _row.join(\"\")))\n return\n }\n for (let col = 0; col < n; col++) {\n if (isValid(row, col)) {\n result[row][col] = \"Q\"\n dfs(row + 1)\n result[row][col] = \".\"\n }\n }\n }\n dfs(0)\n return final\n}\n\nconsole.log(solveNQueens(4))\n",
"url": "https://leetcode.cn/problems/n-queens/description/",
"sortKey": 13
},
{
"name": "14.删除无效的括号.ts",
"type": "file",
"path": "src/7.回溯算法/14.删除无效的括号.ts",
"content": "/**\n * @url https://leetcode.cn/problems/remove-invalid-parentheses/solutions/1068652/gong-shui-san-xie-jiang-gua-hao-de-shi-f-asu8/\n */\n",
"url": "https://leetcode.cn/problems/remove-invalid-parentheses/solutions/1068652/gong-shui-san-xie-jiang-gua-hao-de-shi-f-asu8/",
"sortKey": 14
}
],
"sortKey": 7
},
{
"name": "8.动态规划",
"type": "directory",
"path": "src/8.动态规划",
"children": [
{
"name": "1.斐波那契数.ts",
"type": "file",
"path": "src/8.动态规划/1.斐波那契数.ts",
"content": "/**\n * @url https://leetcode.cn/problems/fibonacci-number/description/\n */\nfunction fib(n: number): number {\n let dp = new Array(n + 1).fill(0)\n dp[0] = 0\n dp[1] = 1\n for (let i = 2; i <= n; i++) {\n dp[i] = dp[i - 1] + dp[i - 2]\n }\n return dp[n]\n}\n",
"url": "https://leetcode.cn/problems/fibonacci-number/description/",
"sortKey": 1
},
{
"name": "2.爬楼梯.ts",
"type": "file",
"path": "src/8.动态规划/2.爬楼梯.ts",
"content": "/**\n * @url https://leetcode.cn/problems/climbing-stairs/description/\n */\nfunction climbStairs(n: number): number {\n const dp = new Array(n + 1).fill(false)\n dp[0] = 0\n dp[1] = 1\n dp[2] = 2\n for (let i = 3; i <= n; i++) {\n dp[i] = dp[i - 1] + dp[i - 2]\n }\n return dp[n]\n}\n",
"url": "https://leetcode.cn/problems/climbing-stairs/description/",
"sortKey": 2
},
{
"name": "3.使用最小花费爬楼梯.ts",
"type": "file",
"path": "src/8.动态规划/3.使用最小花费爬楼梯.ts",
"content": "/**\n * @url https://leetcode.cn/problems/min-cost-climbing-stairs/description/\n */\n\n// notice:注意审题,可以从下标为0,下标为1开始爬\nfunction minCostClimbingStairs(cost: number[]): number {\n const dp = new Array(cost.length + 1).fill(0) // notice:dp表示当前爬下标为n的楼梯所需要的最小花费, 到楼顶表示当前下标要溢出数组\n dp[0] = 0\n dp[1] = 0\n\n for (let i = 2; i <= cost.length; i++) {\n dp[i] = Math.min(dp[i - 2] + cost[i - 2], dp[i - 1] + cost[i - 1])\n }\n console.log(dp)\n\n return dp[dp.length - 1]\n}\n",
"url": "https://leetcode.cn/problems/min-cost-climbing-stairs/description/",
"sortKey": 3
},
{
"name": "4.不同路径.ts",
"type": "file",
"path": "src/8.动态规划/4.不同路径.ts",
"content": "/**\n * @url https://leetcode.cn/problems/unique-paths/description/\n */\n\nfunction uniquePaths(m: number, n: number): number {\n const dp = new Array(m).fill(0).map((_num) => new Array(n).fill(0)) // m * n 的二维数组\n\n // TODO:注意这个是路径,不是走了多少步\n // const initDp = () => {\n // for (let row = 0; row < m; row++) {\n // dp[row][0] = row\n // }\n // for (let col = 0; col < n; col++) {\n // dp[0][col] = col\n // }\n // }\n\n const initDp = () => {\n for (let row = 0; row < m; row++) {\n dp[row][0] = 1\n }\n for (let col = 0; col < n; col++) {\n dp[0][col] = 1\n }\n }\n dp[0][0] = 0\n initDp()\n\n for (let i = 1; i < m; i++) {\n for (let j = 1; j < n; j++) {\n dp[i][j] = dp[i - 1][j] + dp[i][j - 1]\n }\n }\n\n return dp[m - 1][n - 1]\n}\n\nconsole.log(uniquePaths(3, 2))\n",
"url": "https://leetcode.cn/problems/unique-paths/description/",
"sortKey": 4
},
{
"name": "5.不同路径2.ts",
"type": "file",
"path": "src/8.动态规划/5.不同路径2.ts",
"content": "/**\n * @url https://leetcode.cn/problems/unique-paths-ii/description/\n */\n\n// TODO:注意初始化的操作\nfunction uniquePathsWithObstacles(obstacleGrid: number[][]): number {\n const dp = new Array(obstacleGrid.length)\n .fill(0)\n .map((_num) => new Array(obstacleGrid[0].length).fill(0)); // m * n 的二维数组\n if (obstacleGrid[0][0] === 1) return 0;\n const initDp = () => {\n for (let row = 0; row < obstacleGrid.length; row++) {\n if (obstacleGrid[row][0] === 1) {\n dp[row][0] = 0;\n break;\n } else {\n dp[row][0] = 1;\n }\n }\n for (let col = 0; col < obstacleGrid[0].length; col++) {\n if (obstacleGrid[0][col] === 1) {\n dp[0][col] = 0;\n break;\n } else {\n dp[0][col] = 1;\n }\n }\n };\n dp[0][0] = 0;\n initDp();\n\n for (let i = 1; i < obstacleGrid.length; i++) {\n for (let j = 1; j < obstacleGrid[0].length; j++) {\n if (obstacleGrid[i][j] === 1) {\n dp[i][j] = 0;\n } else {\n dp[i][j] = dp[i - 1][j] + dp[i][j - 1];\n }\n }\n }\n console.log(dp);\n\n return dp[obstacleGrid.length - 1][obstacleGrid[0].length - 1];\n}\n\n// ! 暴力解法\n/**\n * @param {number[][]} obstacleGrid\n * @return {number}\n */\nvar uniquePathsWithObstaclesViolent = function (obstacleGrid) {\n // 如果起点或终点是障碍,直接返回0\n if (\n obstacleGrid[0][0] === 1 ||\n obstacleGrid[obstacleGrid.length - 1][obstacleGrid[0].length - 1] === 1\n ) {\n return 0;\n }\n\n const m = obstacleGrid.length;\n const n = obstacleGrid[0].length;\n let pathCount = 0;\n\n // 方向数组:向下和向右\n const directions = [\n [1, 0],\n [0, 1],\n ]; // [row, col]\n\n function backtrack(row, col) {\n // 到达终点,路径数加1\n if (row === m - 1 && col === n - 1) {\n pathCount++;\n return;\n }\n\n // 尝试每个方向\n for (let [dr, dc] of directions) {\n const newRow = row + dr;\n const newCol = col + dc;\n\n // 检查边界和障碍\n if (newRow < m && newCol < n && obstacleGrid[newRow][newCol] !== 1) {\n backtrack(newRow, newCol);\n }\n }\n }\n\n // 从起点开始\n backtrack(0, 0);\n return pathCount;\n};\n\n// 测试用例\nconsole.log(\n uniquePathsWithObstacles([\n [0, 0, 0],\n [0, 1, 0],\n [0, 0, 0],\n ])\n); // 输出 2\n\nconsole.log(\n uniquePathsWithObstacles([\n [0, 1],\n [0, 0],\n ])\n); // 输出 1\n",
"url": "https://leetcode.cn/problems/unique-paths-ii/description/",
"sortKey": 5
},
{
"name": "6.整数拆分.ts",
"type": "file",
"path": "src/8.动态规划/6.整数拆分.ts",
"content": "/**\n * @url https://leetcode.cn/problems/integer-break/description/\n */\n\n// dp[n]表示n拆分至少两个数的最大乘积\nfunction integerBreak(n: number): number {\n const dp = new Array(n + 1).fill(0)\n dp[0] = 0\n dp[1] = 0\n dp[2] = 1\n for (let i = 3; i <= n; i++) {\n // 拆分成0其实没有意义,所以这里拆分成1开始\n for (let j = 1; j < i; j++) {\n dp[i] = Math.max(dp[i], j * (i - j), j * dp[i - j])\n }\n }\n return dp[n]\n}\n",
"url": "https://leetcode.cn/problems/integer-break/description/",
"sortKey": 6
},
{
"name": "7.不同的二叉搜索树.ts",
"type": "file",
"path": "src/8.动态规划/7.不同的二叉搜索树.ts",
"content": "/**\n * @url https://leetcode.cn/problems/unique-binary-search-trees/description/\n * @resolve https://leetcode.cn/problems/unique-binary-search-trees/solutions/6693/hua-jie-suan-fa-96-bu-tong-de-er-cha-sou-suo-shu-b/\n */\n// G[n]代表n个节点的二叉搜索树的个数\n// f[i]代表以i为根节点的二叉搜索树的个数\n\n// G[n] = f(1)+f(2)+....f(n)\n// f(i) = G(i-1)*G(n-i) // 左子树的个数有i-1个,右子树的个数有n-i个\n// G(n) = G(0)*G(n-1)+G(1)*G(1)+.....G(n-1)*G(0)\n\nfunction numTrees(n: number): number {\n const dp = new Array(n + 1).fill(0) // dp[n]表示n个节点的二叉树的个数\n dp[1] = 1\n dp[0] = 1 // 其实没有任何意义\n for (let i = 2; i <= n; i++) {\n for (let j = 1; j <= i; j++) {\n dp[i] += dp[j - 1] * dp[i - j]\n }\n }\n\n return dp[n]\n}\n",
"url": "https://leetcode.cn/problems/unique-binary-search-trees/description/",
"sortKey": 7
},
{
"name": "9.分割等和子集.ts",
"type": "file",
"path": "src/8.动态规划/9.分割等和子集.ts",
"content": "/**\n * @url https://leetcode.cn/problems/partition-equal-subset-sum/description/\n */\n\n// dp[i][j]表示从[0,i]个物品中选取,是否有一种方案恰好能够装满容量j。\n// 提示:\n// 1 <= nums.length <= 200\n// 1 <= nums[i] <= 100\n\n// function canPartition(nums: number[]): boolean {\n// const target = nums.reduce((acc, cur) => acc + cur, 0) / 2\n// if (!Number.isInteger(target)) return false\n\n// const dp: boolean[][] = new Array(nums.length).fill(false).map((_arr) => new Array(target + 1).fill(false))\n\n// dp[0][nums[0]] = true\n\n// for (let row = 0; row < nums.length; row++) {\n// dp[row][0] = true\n// }\n\n// for (let row = 1; row < nums.length; row++) {\n// for (let col = 1; col < target + 1; col++) {\n// if (col - nums[row] >= 0) {\n// dp[row][col] = dp[row - 1][col] || dp[row - 1][col - nums[row]]\n// } else {\n// dp[row][col] = dp[row - 1][col]\n// }\n// }\n// }\n\n// return dp[nums.length - 1][target]\n// }\n\n//TODO:优化 dp[j]表示是否有一种方案能够装满dp[j]\nfunction canPartition(nums: number[]): boolean {\n const sum = nums.reduce((acc, cur) => acc + cur);\n const capaticy = sum / 2;\n if (!Number.isInteger(capaticy)) {\n return false;\n }\n const dp: boolean[] = new Array(capaticy + 1).fill(false); // 数组下标和容量的差距\n\n dp[0] = true;\n for (let row = 0; row < nums.length; row++) {\n for (let col = capaticy; col >= nums[row]; col--) {\n dp[col] = dp[col] || dp[col - nums[row]];\n }\n }\n return dp[capaticy];\n}\n",
"url": "https://leetcode.cn/problems/partition-equal-subset-sum/description/",
"sortKey": 9
},
{
"name": "10.最后一块石头的重量 II.ts",
"type": "file",
"path": "src/8.动态规划/10.最后一块石头的重量 II.ts",
"content": "/**\n * @url https://leetcode.cn/problems/last-stone-weight-ii/description/\n */\n\n// TODO:如何将问题抽象成01背包问题?\n\n// 要使最后一块石头的重量尽可能地小,neg 需要在不超过 ⌊sum/2⌋ 的前提下尽可能地大。因此本问题可以看作是背包容量为 ⌊sum/2⌋,物品重量和价值均为 stones\n\n// function lastStoneWeightII(stones: number[]): number {\n// const sum = stones.reduce((acc, cur) => acc + cur, 0)\n// const target = Math.floor(sum / 2)\n// const dp = new Array(stones.length).fill(0).map((_arr) => new Array(target + 1).fill(0))\n// for (let i = 0; i < stones.length; i++) {\n// dp[i][0] = 0\n// }\n// for (let j = 0; j <= target; j++) {\n// if (j >= stones[0]) {\n// dp[0][j] = stones[0]\n// } else {\n// dp[0][j] = 0\n// }\n// }\n\n// for (let row = 1; row < stones.length; row++) {\n// for (let col = 1; col <= target; col++) {\n// if (col - stones[row] >= 0) {\n// dp[row][col] = Math.max(dp[row - 1][col], dp[row - 1][col - stones[row]] + stones[row])\n// } else {\n// dp[row][col] = dp[row - 1][col]\n// }\n// }\n// }\n// return Math.abs(sum - dp[stones.length - 1][target] - dp[stones.length - 1][target])\n// }\n\n// notice:抽离成一维数组\nfunction lastStoneWeightII(stones: number[]): number {\n const sum = stones.reduce((acc, cur) => acc + cur, 0);\n const target = Math.floor(sum / 2);\n\n const dp: number[] = new Array(target + 1).fill(0);\n dp[0] = 0;\n\n for (let row = 0; row < stones.length; row++) {\n console.dir(dp);\n\n for (let col = target; col >= stones[row]; col--) {\n dp[col] = Math.max(dp[col - stones[row]] + stones[row], dp[col]);\n }\n }\n\n console.dir(dp);\n return Math.abs(sum - dp[target] - dp[target]);\n}\n\nlastStoneWeightII([2, 7, 4, 1, 8, 1]);\n",
"url": "https://leetcode.cn/problems/last-stone-weight-ii/description/",
"sortKey": 10
},
{
"name": "11.目标和.ts",
"type": "file",
"path": "src/8.动态规划/11.目标和.ts",
"content": "/**\n * @url https://leetcode.cn/problems/target-sum/description/\n */\n// left + right = sum\n// left - right = target\n// left = (sum + target) / 2\n\n// TODO:初始化很难\n// notice:01背包应用之“有多少种不同的填满背包最大容量的方法“\n// function findTargetSumWays(nums: number[], target: number): number {\n// let sum = nums.reduce((acc, cur) => acc + cur, 0)\n// if (Math.abs(target) > sum) return 0\n// let beibao = (sum + target) / 2\n// if (!Number.isInteger(beibao)) return 0\n// if (target > sum) return 0\n// const dp = new Array(nums.length).fill(0).map((_arr) => new Array(beibao + 1 || 1).fill(0))\n\n// dp[0][nums[0]] = 1 // 注意这个顺序\n\n// // 背包容量为0,只要出现了0,就可以代表2^n\n\n// let numZeros = 0\n// for (let i = 0; i < nums.length; i++) {\n// if (nums[i] == 0) {\n// numZeros++\n// }\n// dp[i][0] = Math.pow(2, numZeros)\n// }\n\n// for (let i = 1; i < nums.length; i++) {\n// for (let j = 1; j < beibao + 1; j++) {\n// if (j - nums[i] < 0) {\n// dp[i][j] = dp[i - 1][j]\n// } else {\n// dp[i][j] = dp[i - 1][j] + dp[i - 1][j - nums[i]]\n// }\n// }\n// }\n// console.table(dp)\n\n// // console.log(dp)\n// return dp[nums.length - 1][beibao]\n// }\n\n// TODO:优化 一维数组\n// left + right = target left -right = sum left = (sum + target) / 2\n// dp[target] 装满背包容量target有多少种方法\nfunction findTargetSumWays(nums: number[], target: number): number {\n const sum = nums.reduce((acc, cur) => acc + cur, 0)\n const capacity = (sum + target) / 2\n if (!Number.isInteger(capacity)) return 0\n if (Math.abs(target) > sum) return 0\n const dp = new Array(capacity + 1).fill(0)\n dp[0] = 1 // 装满背包容量为0,可以什么都不装也为一种方案\n for (let row = 0; row < nums.length; row++) {\n console.log(dp)\n for (let col = capacity; col >= nums[row]; col--) {\n dp[col] = dp[col - nums[row]] + dp[col]\n }\n }\n\n return dp[capacity]\n}\n\nfindTargetSumWays([1, 2, 1, 2], 4)\n",
"url": "https://leetcode.cn/problems/target-sum/description/",
"sortKey": 11
},
{
"name": "12.一和零.ts",
"type": "file",
"path": "src/8.动态规划/12.一和零.ts",
"content": "/**\n * @url https://leetcode.cn/problems/ones-and-zeroes/description/\n */\n\n// dp表示不超过容量的情况下,有多少个子集\n// !本质还是01背包,维度从1维变成2维了,0 和 1\nfunction findMaxForm(strs: string[], m: number, n: number): number {\n const dp = new Array(m + 1).fill(0).map((_v) => new Array(n + 1).fill(0));\n\n dp[0][0] = 0;\n\n const getMandN = (str: string) => {\n let m = 0,\n n = 0;\n for (let i = 0; i < str.length; i++) {\n if (str[i] === \"1\") {\n n++;\n } else {\n m++;\n }\n }\n return {\n m,\n n,\n };\n };\n\n for (let i = 0; i < strs.length; i++) {\n const { m: strm, n: strn } = getMandN(strs[i]);\n for (let j = m; j >= strm; j--) {\n for (let k = n; k >= strn; k--) {\n dp[j][k] = Math.max(dp[j - strm][k - strn] + 1, dp[j][k]);\n }\n }\n }\n\n return dp[m][n];\n}\n\nfindMaxForm([\"10\", \"0001\", \"111001\", \"1\", \"0\"], 5, 3);\n",
"url": "https://leetcode.cn/problems/ones-and-zeroes/description/",
"sortKey": 12
},
{
"name": "14.零钱兑换二.ts",
"type": "file",
"path": "src/8.动态规划/14.零钱兑换二.ts",
"content": "/**\n * @url https://leetcode.cn/problems/coin-change-ii/description/\n */\n\n// notice:背包容量为j的最大价值是否能为j,物品可以被选取多少次\n// TODO:遍历顺序的考量。dp概念的理解\n// []如果求组合数就是外层for循环遍历物品,内层for遍历背包。\n// []如果求排列数就是外层for遍历背包,内层for循环遍历物品。\nfunction change(amount: number, coins: number[]): number {\n const dp = new Array(amount + 1).fill(0);\n dp[0] = 1;\n for (let i = 0; i < coins.length; i++) {\n for (let j = coins[i]; j <= amount; j++) {\n dp[j] += dp[j - coins[i]];\n }\n }\n console.log(dp);\n\n return dp[amount];\n}\n\nchange(5, [1, 2, 5]);\n",
"url": "https://leetcode.cn/problems/coin-change-ii/description/",
"sortKey": 14
},
{
"name": "15.组合总和四.ts",
"type": "file",
"path": "src/8.动态规划/15.组合总和四.ts",
"content": "/**\n * @url https://leetcode.cn/problems/combination-sum-iv/description/\n */\n\n// dp[j]表示装满背包为j的元素组合的个数\n// notice:其实这个地方就是一个排列\n\nfunction combinationSum4(nums: number[], target: number): number {\n const dp = new Array(target + 1).fill(0)\n dp[0] = 1\n for (let j = 0; j < target + 1; j++) {\n for (let i = 0; i < nums.length; i++) {\n if (j - nums[i] >= 0) {\n dp[j] += dp[j - nums[i]]\n }\n }\n }\n\n return dp[target]\n}\n\nconsole.log(combinationSum4([1, 2, 3], 4))\n\n// TODO: 暴力算出所有的组合",
"url": "https://leetcode.cn/problems/combination-sum-iv/description/",
"sortKey": 15
},
{
"name": "16.零钱兑换.ts",
"type": "file",
"path": "src/8.动态规划/16.零钱兑换.ts",
"content": "/**\n * @url https://leetcode.cn/problems/coin-change/description/\n */\n\n// dp[j]表示刚好装满容量为j的最少硬币个数\nfunction coinChange(coins: number[], amount: number): number {\n const dp = new Array(amount + 1).fill(Number.MAX_SAFE_INTEGER) // notice:初始化的时候:保证能够不是最初的小值\n dp[0] = 0\n for (let i = 0; i < coins.length; i++) {\n for (let j = coins[i]; j <= amount; j++) {\n dp[j] = Math.min(dp[j], dp[j - coins[i]] + 1)\n }\n }\n console.log(dp)\n return dp[amount] === Number.MAX_SAFE_INTEGER ? -1 : dp[amount]\n}\n",
"url": "https://leetcode.cn/problems/coin-change/description/",
"sortKey": 16
},
{
"name": "17.完全平方数.ts",
"type": "file",
"path": "src/8.动态规划/17.完全平方数.ts",
"content": "/**\n * @url https://leetcode.cn/problems/perfect-squares/description/\n */\n\n// dp[n]表示容量为n的情况下,最少需要多少整数来组成n\nfunction numSquares(n: number): number {\n const dp = new Array(n + 1).fill(Number.MAX_SAFE_INTEGER)\n dp[0] = 0\n for (let i = 1; i <= Math.floor(Math.sqrt(n)); i++) {\n for (let j = Math.pow(i, 2); j <= n; j++) {\n dp[j] = Math.min(dp[j], dp[j - Math.pow(i, 2)] + 1)\n }\n }\n return dp[n]\n}\n",
"url": "https://leetcode.cn/problems/perfect-squares/description/",
"sortKey": 17
},
{
"name": "18.单词拆分.ts",
"type": "file",
"path": "src/8.动态规划/18.单词拆分.ts",
"content": "/**\n * @url https://leetcode.cn/problems/word-break/description/\n */\n\n// 排列还是组合,遍历顺序的考量 dp[j]表示长度为j的字符串是否能够被拼接\n// !notice:这一维度的赋值\nfunction wordBreak(s: string, wordDict: string[]): boolean {\n const dp = new Array(s.length + 1).fill(false);\n dp[0] = true;\n\n for (let j = 1; j <= s.length; j++) {\n // 这个for循环每次都会对dp进行覆盖操作\n for (let i = 0; i < wordDict.length; i++) {\n const word = wordDict[i];\n if (j >= word.length) {\n const word1 = s.slice(j - word.length, j);\n if (word === word1 && dp[j - word.length]) {\n dp[j] = true;\n break; // 如果不加break,后面遍历上来,可能会把他给覆盖。\n } else {\n dp[j] = false;\n }\n } else {\n dp[j] = false;\n }\n }\n }\n\n return dp[s.length];\n}\n\nwordBreak(\"applepenapple\", [\"apple\", \"pen\"]);\n\n// !notice:暴力回溯递归解法\nfunction wordBreak1(s, wordDict) {}\n\n// const wordBreak = (s, wordDict) => {\n// let dp = Array(s.length + 1).fill(false);\n// dp[0] = true;\n\n// for (let i = 0; i <= s.length; i++) {\n// for (let j = 0; j < wordDict.length; j++) {\n// if (i >= wordDict[j].length) {\n// if (\n// s.slice(i - wordDict[j].length, i) === wordDict[j] &&\n// dp[i - wordDict[j].length]\n// ) {\n// dp[i] = true;\n// }\n// }\n// }\n// }\n\n// return dp[s.length];\n// };\n",
"url": "https://leetcode.cn/problems/word-break/description/",
"sortKey": 18
},
{
"name": "19.打家劫舍.ts",
"type": "file",
"path": "src/8.动态规划/19.打家劫舍.ts",
"content": "export {};\n/**\n * @url https://leetcode.cn/problems/house-robber/description/\n */\n\n// dp[i]表示偷窃第i+1间房屋所能获得的最大金额\n// dp[2] = dp[0]+cur, dp[1]\n// dp[3] = dp[1]+cur,dp[2]\n// dp[n] = dp[n-2]+cur dp[n-1]\n// !notice:因为题目中都是正数,所以这里不需要考虑前面(),dp[i-1]一定是没有选择i的最大值,实际上也是运用到了一点贪心的策略\nfunction rob(nums: number[]): number {\n const dp = new Array(nums.length).fill(0);\n dp[0] = nums[0];\n if (nums.length === 1) {\n return dp[0];\n }\n dp[1] = Math.max(nums[0], nums[1]);\n for (let i = 2; i < nums.length; i++) {\n dp[i] = Math.max(dp[i - 2] + nums[i], dp[i - 1], dp[i]);\n }\n\n return dp[nums.length - 1];\n}\n\n/**\n * @url https://leetcode.cn/problems/house-robber/description/\n */\n\n// dp[i]表示偷窃第i+1间房屋所能获得的最大金额,\n// dp[2] = dp[0]+cur, dp[1]\n// dp[3] = dp[1]+cur,dp[2]\n// dp[n] = dp[n-2]+cur dp[n-1]\nfunction rob1(nums: number[]): number {\n const dp = new Array(nums.length).fill(0);\n dp[0] = nums[0];\n if (nums.length === 1) {\n return dp[0];\n }\n dp[1] = Math.max(nums[0], nums[1]);\n for (let i = 2; i < nums.length; i++) {\n for (let j = 0; j < i - 1; j++) {\n // dp[j] + nums[i] 表示需要当天的\n // dp[i - 1] 不要当天\n // dp[i] 对比\n dp[i] = Math.max(dp[j] + nums[i], dp[i], dp[i - 1]);\n }\n }\n\n return dp[nums.length - 1];\n}\n",
"url": "https://leetcode.cn/problems/house-robber/description/",
"sortKey": 19
},
{
"name": "20.打家劫舍二.ts",
"type": "file",
"path": "src/8.动态规划/20.打家劫舍二.ts",
"content": "/**\n * @url https://leetcode.cn/problems/house-robber-ii/description/\n */\n\n// 偷了1就不能够偷最后一个,偷了最后一个就不能偷第一个\n// TODO:dp[i]表示到达第[i+1]房屋所能偷到的最大额度\nfunction rob(nums: number[]): number {\n const dp1 = new Array(nums.length - 1).fill(0)\n const dp2 = new Array(nums.length - 1).fill(0)\n if (nums.length === 1) return nums[0]\n dp1[0] = nums[0]\n dp1[1] = Math.max(nums[0], nums[1])\n dp2[0] = nums[1]\n dp2[1] = Math.max(nums[1], nums[2])\n\n const nums1 = nums.concat([])\n const nums2 = nums.concat([])\n nums1.pop()\n nums2.shift()\n for (let i = 2; i < nums1.length; i++) {\n dp1[i] = Math.max(dp1[i - 2] + nums1[i], dp1[i - 1])\n }\n for (let i = 2; i < nums2.length; i++) {\n dp2[i] = Math.max(dp2[i - 2] + nums2[i], dp2[i - 1])\n }\n\n console.log(dp1, dp2)\n\n return Math.max(dp1[nums.length - 2], dp2[nums.length - 2])\n}\nconsole.log(rob([1, 3, 2]))\n",
"url": "https://leetcode.cn/problems/house-robber-ii/description/",
"sortKey": 20
},
{
"name": "21.打家劫舍三.ts",
"type": "file",
"path": "src/8.动态规划/21.打家劫舍三.ts",
"content": "/**\n * @url https://leetcode.cn/problems/house-robber-iii/description/\n */\nexport {} // 表明是一个模块,防止ts报错\n\n// TODO:树形dp,与数组的区别是定义每个节点的一个状态\nclass TreeNode {\n val: number\n left: TreeNode | null\n right: TreeNode | null\n constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {\n this.val = val === undefined ? 0 : val\n this.left = left === undefined ? null : left\n this.right = right === undefined ? null : right\n }\n}\n\n// dp[0]表示当前节点未偷 dp[1]表示当前节点已经被偷\n// function rob(root: TreeNode | null): number {\n// const traverse = (root: TreeNode | null): Array<number> => {\n// const dp: number[] = new Array(2).fill(0)\n\n// if (!root) return dp.concat([])\n// if (!root.left && !root.right) {\n// dp[1] = root.val\n// return dp.concat()\n// }\n// // 节点被偷\n// dp[1] = root.val + traverse(root.left)[0] + traverse(root.right)[0]\n\n// // 节点未被偷,但也不一定是会去子节点的最大值呀!!!!\n// dp[0] = traverse(root.left)[1] + traverse(root.right)[1]\n\n// return dp.concat([])\n// }\n\n// return Math.max(...traverse(root))\n// }\n\nfunction rob(root: TreeNode | null): number {\n const traverse = (root: TreeNode | null): Array<number> => {\n const dp: number[] = new Array(2).fill(0)\n\n if (!root) return dp.concat([])\n if (!root.left && !root.right) {\n dp[1] = root.val\n return dp.concat()\n }\n\n // 后序遍历\n const left = traverse(root.left)\n const right = traverse(root.right)\n\n // 节点被偷\n dp[1] = root.val + left[0] + right[0]\n\n // 节点未被偷,但也不一定是会去子节点的最大值呀!!!!\n dp[0] = Math.max(...left) + Math.max(...right)\n\n return dp.concat([])\n }\n\n return Math.max(...traverse(root))\n}\n",
"url": "https://leetcode.cn/problems/house-robber-iii/description/",
"sortKey": 21
},
{
"name": "22.买卖股票的最佳时机.ts",
"type": "file",
"path": "src/8.动态规划/22.买卖股票的最佳时机.ts",
"content": "/**\n * @url https://leetcode.cn/problems/best-time-to-buy-and-sell-stock/description/\n */\nexport {};\n// [7,1,5,3,6,4] 5\n// 暴力解法--超时\n// function maxProfit(prices: number[]): number {\n// let max = Number.MIN_SAFE_INTEGER\n// for (let i = 0; i < prices.length; i++) {\n// for (let j = i + 1; j < prices.length; j++) {\n// max = Math.max(max, prices[j] - prices[i])\n// }\n// }\n// return max < 0 ? 0 : max\n// }\n\n// dp[i][0] 表示 第i天持有股票所拥有的最大金额\n// 保持上一次的持有股票状态\n// 刚买入\n// dp[i][1] 表示 第i天不持有股票所拥有的最大金额\n// 上一次没有持有股票的状态\n// 按照今天的股价卖出\n\n// !notice:表示一天时间\nfunction maxProfit(prices: number[]): number {\n const dp = new Array(prices.length)\n .fill(0)\n .map((_item) => new Array(2).fill(0));\n dp[0][0] = -prices[0];\n dp[0][1] = 0;\n for (let i = 1; i < prices.length; i++) {\n dp[i][0] = Math.max(dp[i - 1][0], -prices[i]); // !notice:这里-price[i]不依赖于之前的状态。因为题目限定只需要执行一次,具体可参照 23.买股票的最佳时机二.ts\n dp[i][1] = Math.max(dp[i - 1][1], prices[i] + dp[i - 1][0]);\n }\n return dp[prices.length - 1][1];\n}\n\n// TODO:一维数组优化\n\n// note:思路参考背包问题\nfunction maxProfitOne(prices: number[]): number {\n const dp = new Array(2).fill(Number.MIN_SAFE_INTEGER);\n for (let i = 0; i < prices.length; i++) {\n // dp[i][0] = Math.max(dp[i - 1][0], -prices[i])\n // dp[i][1] = Math.max(dp[i - 1][1], prices[i] + dp[i - 1][0])\n // TODO:这里本身会被覆盖,因此需要用两层递归\n dp[0] = Math.max(dp[0], -prices[i]);\n dp[1] = Math.max(dp[1], dp[0] + prices[i]);\n }\n return dp[1];\n}\n",
"url": "https://leetcode.cn/problems/best-time-to-buy-and-sell-stock/description/",
"sortKey": 22
},
{
"name": "23.买股票的最佳时机二.ts",
"type": "file",
"path": "src/8.动态规划/23.买股票的最佳时机二.ts",
"content": "/**\n * @url https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-ii/description/\n */\n// dp[i][0]表示第i天所拥有的最大利润\n// dp[i][0]表示第i天持有股票所拥有的最大利润\n// dp[i][1]表示第i天不持有股票所拥有的最大利润\nfunction maxProfit(prices: number[]): number {\n const dp: number[] = new Array(2).fill(0)\n dp[0] = -prices[0]\n dp[1] = 0\n for (let i = 1; i < prices.length; i++) {\n // 这里会被覆盖,可以用两层数组来保存\n dp[0] = Math.max(dp[0], dp[1] - prices[i])\n dp[1] = Math.max(dp[1], dp[0] + prices[i])\n }\n return dp[1]\n}\n",
"url": "https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-ii/description/",
"sortKey": 23
},
{
"name": "24.买股票的最佳时机三.ts",
"type": "file",
"path": "src/8.动态规划/24.买股票的最佳时机三.ts",
"content": "/**\n * @url https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-iii/description/\n */\nexport {};\n\n// TODO:最多完成几笔交易,状态怎么办?想想前两次股票递推的第二个参数的意思 和前面两题是一样的\n\n// dp[i][0]表示在第i天中,第一次买入股票的最大利润 dp[i][0] = -price[i]\n// dp[i][1]表示在第i天中,第一次卖出股票的最大利润 dp[i][1] = dp[i-1]+price[i]\n// dp[i][2]表示在第i天中,第二次买入股票的最大利润 dp[i][2] = dp[i-1][1]-price[i]\n// dp[i][3]表示在第i天中,第二次卖出股票的最大利润 dp[i][3] = dp[i-1][2]+price[i]\nfunction maxProfit(prices: number[]): number {\n const dp = new Array(prices.length)\n .fill(0)\n .map((_arr) => new Array(4).fill(0));\n dp[0][0] = -prices[0];\n dp[0][1] = 0;\n dp[0][2] = -prices[0];\n dp[0][3] = 0;\n for (let i = 1; i < prices.length; i++) {\n dp[i][0] = Math.max(dp[i - 1][0], -prices[i]);\n dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] + prices[i]);\n dp[i][2] = Math.max(dp[i - 1][2], dp[i - 1][1] - prices[i]);\n dp[i][3] = Math.max(dp[i - 1][3], dp[i - 1][2] + prices[i]);\n }\n return dp[prices.length - 1][3];\n}\n\n// TODO:空间优化\nfunction maxProfit1(prices: number[]): number {\n const dp = new Array(4).fill(0);\n dp[0] = -prices[0];\n dp[1] = 0;\n dp[2] = -prices[0];\n dp[3] = 0;\n for (let i = 1; i < prices.length; i++) {\n dp[i][0] = Math.max(dp[0], -prices[i]);\n dp[i][1] = Math.max(dp[1], dp[0] + prices[i]);\n dp[i][2] = Math.max(dp[2], dp[1] - prices[i]);\n dp[i][3] = Math.max(dp[3], dp[2] + prices[i]);\n }\n return dp[3];\n}\n",
"url": "https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-iii/description/",
"sortKey": 24
},
{
"name": "25.买股票的最佳时机四.ts",
"type": "file",
"path": "src/8.动态规划/25.买股票的最佳时机四.ts",
"content": "/**\n * @url https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-iv/description/\n */\n\nexport {}\n\n// 两笔交易\n// dp[i][0]表示在第i天中,第一次买入股票的最大利润 dp[i][0] = dp[i-1][0],-price[i]\n// dp[i][1]表示在第i天中,第一次卖出股票的最大利润 dp[i][1] = dp[i-1][1],dp[i-1][0]+price[i]\n// dp[i][2]表示在第i天中,第二次买入股票的最大利润 dp[i][2] = dp[i-1][2],dp[i-1][1]-price[i]\n// dp[i][3]表示在第i天中,第二次卖出股票的最大利润 dp[i][3] = dp[i-1][3],dp[i-1][2]+price[i]\nfunction maxProfit(k: number, prices: number[]): number {\n const dp: number[][] = new Array(prices.length).fill(0).map((_item) => new Array(2 * k).fill(0))\n for (let i = 0; i < 2 * k; i++) {\n if (i % 2 === 0) {\n dp[0][i] = -prices[0]\n } else {\n dp[0][i] = 0\n }\n }\n for (let i = 1; i < prices.length; i++) {\n for (let j = 0; j < 2 * k; j++) {\n if (j === 0) {\n dp[i][j] = Math.max(dp[i - 1][0], -prices[i])\n } else {\n if (j % 2 === 0) {\n dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - 1] - prices[i])\n } else {\n dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - 1] + prices[i])\n }\n }\n }\n }\n return dp[prices.length - 1][2 * k - 1]\n}\n",
"url": "https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-iv/description/",
"sortKey": 25
},
{
"name": "26.买卖股票的最佳时机含冷冻期.ts",
"type": "file",
"path": "src/8.动态规划/26.买卖股票的最佳时机含冷冻期.ts",
"content": "/**\n * @url https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-with-cooldown/description/\n */\n\n// dp[i][0]表示第i天手上有股票时候的能够获取的最大利润\n// dp[i][1]表示第i天手上没有股票时候的能够获取的最大利润\n// dp[i][2]表示第i天处于冷冻期的时候的最大利润\n\n// 状态还是没考虑全\n// function maxProfit(prices: number[]): number {\n// const dp = new Array(prices.length).fill(0).map((_item) => new Array(3).fill(0))\n// dp[0][0] = -prices[0]\n// dp[0][1] = 0\n// dp[0][0] = 0\n\n// for (let i = 1; i < prices.length; i++) {\n// dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] - prices[i], dp[i - 1][2] - prices[i])\n// dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] + prices[i], dp[i - 1][2])\n// dp[i][2] = Math.max(dp[i - 1][0] + prices[i], dp[i - 1][2])\n// }\n// return Math.max(...dp[prices.length - 1])\n// }\n\n// TODO:再来做一遍\n// notice:关键点:不持有股票的状态拆分出来\n// 初始化如果非法的话,可以从递推公式来看具体初始化多少\n// dp[i][0]表示持有股票的最大利润 dp[i-1][0],dp[i-1][2]-price[i],dp[i-1][3]-price[i]\n// Note:备注一下 dp[i-1][3]-price[i]表示前一天是冷冻期,然后今天买入股票\n// dp[i][1]表示今天卖出股票,下一天是冷冻期 dp[i-1][0]+price[i]\n// dp[i][2]表示冷冻期之后保持卖出的状态 dp[i-1][2],dp[i-1][3]\n// dp[i][3]表示冷冻期的状态 dp[i-1][1]\nfunction maxProfit(prices: number[]): number {\n const dp = new Array(prices.length).fill(0).map((_v) => new Array(4).fill(0));\n dp[0][0] = -prices[0];\n dp[0][1] = 0;\n dp[0][2] = 0;\n dp[0][3] = 0;\n for (let i = 1; i < prices.length; i++) {\n dp[i][0] = Math.max(\n dp[i - 1][0],\n dp[i - 1][2] - prices[i],\n dp[i - 1][3] - prices[i]\n );\n dp[i][1] = dp[i - 1][0] + prices[i];\n dp[i][2] = Math.max(dp[i - 1][2], dp[i - 1][3]);\n dp[i][3] = dp[i - 1][1];\n }\n return Math.max(...dp[prices.length - 1]);\n}\n\n// 这里如果要做空间优化,需要去做赋值顺序的调整\n",
"url": "https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-with-cooldown/description/",
"sortKey": 26
},
{
"name": "27.买卖股票的最佳时机含手续费.ts",
"type": "file",
"path": "src/8.动态规划/27.买卖股票的最佳时机含手续费.ts",
"content": "/**\n * @url https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/description/\n */\nexport {};\n\n// dp[i][0]表示持有股票拥有的最大利润\n// dp[i][1]表示不持有股票拥有的最大利润\n// TODO:探索一下如何debugger,因为目前dp状态越来越复杂,只能手动模拟数值了。然后看对应的。\nfunction maxProfit(prices: number[], fee: number): number {\n const dp = new Array(prices.length).fill(0).map((_v) => new Array(2).fill(0));\n dp[0][0] = -prices[0];\n dp[0][1] = 0;\n for (let i = 1; i < prices.length; i++) {\n dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] - prices[i]);\n dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] + prices[i] - fee);\n }\n console.table(dp);\n\n return dp[prices.length - 1][1];\n}\n\nmaxProfit([1, 3, 2, 8, 4, 9], 2);\n",
"url": "https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/description/",
"sortKey": 27
},
{
"name": "28.最长递增子序列.ts",
"type": "file",
"path": "src/8.动态规划/28.最长递增子序列.ts",
"content": "/**\n * @url https://leetcode.cn/problems/longest-increasing-subsequence/description/\n */\n// function lengthOfLIS(nums: number[]): number {}\n\n// notice:找出所有的递增子序列,回溯法暴力搜索\n// TODO:思考一下暴力搜索\n\n// function lengthOfLIS(nums: number[]): number {\n// const res: number[][] = []\n// const dfs = (path: number[], startIndex: number) => {\n// const set: Set<number> = new Set()\n\n// if (path[path.length - 1] <= path[path.length - 2]) {\n// path.pop()\n// res.push(path.concat())\n// return\n// }\n// if (startIndex >= nums.length) {\n// res.push(path.concat())\n// return\n// }\n// for (let i = startIndex; i < nums.length; i++) {\n// if (set.has(nums[i])) {\n// continue\n// }\n\n// path.push(nums[i])\n// set.add(nums[i])\n// dfs(path.concat(), i + 1)\n// path.pop()\n// }\n// }\n// const findMax = (arr: number[][]) => {\n// let max = Number.MIN_SAFE_INTEGER\n// arr.forEach((_v) => {\n// max = Math.max(_v.length, max)\n// })\n// return max\n// }\n// dfs([], 0)\n\n// return findMax(res)\n// }\n\n// console.log(lengthOfLIS([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]))\n\n// TODO:动态规划\n// dp[i]表示到达第i个位置所能获得的最长递增子序列\n// dp[i] = dp[j]+1 nums[i] > nums[j]\nfunction lengthOfLIS(nums: number[]): number {\n const dp = new Array(nums.length).fill(1); // 初始化的时候注意一点\n for (let i = 0; i < nums.length; i++) {\n for (let j = 0; j < i; j++) {\n if (nums[i] > nums[j]) {\n dp[i] = Math.max(dp[j] + 1, dp[i]);\n }\n }\n }\n return Math.max(...dp);\n}\n// console.log(lengthOfLIS([1, 3, 6, 7, 9, 4, 10, 5, 6]))\n",
"url": "https://leetcode.cn/problems/longest-increasing-subsequence/description/",
"sortKey": 28
},
{
"name": "29.最长连续递增子序列.ts",
"type": "file",
"path": "src/8.动态规划/29.最长连续递增子序列.ts",
"content": "// @ts-nocheck\n/**\n * @url https://leetcode.cn/problems/longest-continuous-increasing-subsequence/description/\n */\n\n// dp[i]表示到第i个位置的连续子序列的长度\nfunction findLengthOfLCIS(nums: number[]): number {\n const dp = new Array(nums.length).fill(1);\n for (let i = 1; i < nums.length; i++) {\n if (nums[i] > nums[i - 1]) {\n dp[i] = Math.max(dp[i - 1] + 1, dp[i]);\n }\n }\n return Math.max(...dp);\n}\n\n// ps:暴力解法如下\n\nfunction lengthOfLIS(nums) {\n if (!nums || nums.length === 0) return 0;\n\n let maxLen = 0; // 全局变量,记录最长递增子序列长度\n\n function backtrack(index, currSeq) {\n // 更新最长长度\n maxLen = Math.max(maxLen, currSeq.length);\n\n // 从当前索引往后遍历\n for (let i = index; i < nums.length; i++) {\n // 如果当前元素可以加入递增子序列\n if (currSeq.length === 0 || nums[i] > currSeq[currSeq.length - 1]) {\n // 选择:加入当前元素\n currSeq.push(nums[i]);\n // 递归探索后续元素\n backtrack(i + 1, currSeq);\n // 回溯:撤销选择\n currSeq.pop();\n }\n // 不选择当前元素的情况会自动通过循环继续\n }\n }\n\n backtrack(0, []);\n return maxLen;\n}\n",
"url": "https://leetcode.cn/problems/longest-continuous-increasing-subsequence/description/",
"sortKey": 29
},
{
"name": "30.最长重复子数组.ts",
"type": "file",
"path": "src/8.动态规划/30.最长重复子数组.ts",
"content": "/**\n * @url https://leetcode.cn/problems/maximum-length-of-repeated-subarray/description/\n */\n\n// TODO:dp[i][j]表示以num1[i]为末尾项,末尾项为nums2[j]的子数组\n// PS:注意一下dp[i][j]的定义,dp[i][j]表示以num1[i]为末尾项,末尾项为nums2[j]的子数组\n// nums[i] nums[j]\n// 优化点:init初始化可以简洁一点,不用手动去初始化\n\nfunction findLength(nums1: number[], nums2: number[]): number {\n const dp = new Array(nums1.length)\n .fill(0)\n .map((_v) => new Array(nums2.length).fill(0));\n let res = Number.MIN_SAFE_INTEGER;\n for (let i = 0; i < nums1.length; i++) {\n if (nums1[i] === nums2[0]) {\n dp[i][0] = 1;\n res = 1;\n }\n }\n for (let i = 0; i < nums2.length; i++) {\n if (nums2[i] === nums1[0]) {\n dp[0][i] = 1;\n res = 1;\n }\n }\n\n for (let i = 1; i < nums1.length; i++) {\n for (let j = 1; j < nums2.length; j++) {\n if (nums1[i] === nums2[j]) {\n dp[i][j] = dp[i - 1][j - 1] + 1;\n }\n res = Math.max(res, dp[i][j]);\n }\n }\n console.table(dp);\n return res === Number.MIN_SAFE_INTEGER ? 0 : res;\n}\nfindLength([1, 2, 3, 2, 1], [3, 2, 1, 2, 7]);\n\n// 12321\n// 32127\n",
"url": "https://leetcode.cn/problems/maximum-length-of-repeated-subarray/description/",
"sortKey": 30
},
{
"name": "31.最长公共子序列.ts",
"type": "file",
"path": "src/8.动态规划/31.最长公共子序列.ts",
"content": "/**\n * @url https://leetcode.cn/problems/longest-common-subsequence/description/\n */\n\n// dp[i][j] 表示text1以i-1为下标,text2以j-1为下标的最长公共子序列\n// dp[i][j] = dp[i-1][j-1] + 1\n// TODO:30,31再好好思索一下 连续性和不连续的区别, 可以通过思考下一个状态的依赖来思考变化\n// - 状态被截断\n// - 状态已经截断\n// - 初始化和最长重复子数组不太一样\nfunction longestCommonSubsequence(text1: string, text2: string): number {\n const dp = new Array(text1.length + 1)\n .fill(0)\n .map((_v) => new Array(text2.length + 1).fill(0));\n let res = Number.MIN_SAFE_INTEGER;\n for (let i = 1; i <= text1.length; i++) {\n for (let j = 1; j <= text2.length; j++) {\n if (text1[i - 1] === text2[j - 1]) {\n dp[i][j] = dp[i - 1][j - 1] + 1;\n } else {\n dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);\n }\n res = Math.max(res, dp[i][j]);\n }\n }\n\n return res;\n}\nlongestCommonSubsequence(\"abc\", \"def\");\n",
"url": "https://leetcode.cn/problems/longest-common-subsequence/description/",
"sortKey": 31
},
{
"name": "32.最大子数组和.ts",
"type": "file",
"path": "src/8.动态规划/32.最大子数组和.ts",
"content": "/**\n * @url https://leetcode.cn/problems/maximum-subarray/description/\n */\n\n// dp[i]表示下标为i结尾的子数组的最大和\n// dp[i] = dp[i-1],\nfunction maxSubArray(nums: number[]): number {\n const dp = new Array(nums.length).fill(0)\n dp[0] = nums[0]\n for (let i = 1; i < nums.length; i++) {\n dp[i] = Math.max(dp[i - 1] + nums[i], nums[i])\n }\n console.log(dp)\n return Math.max(...dp)\n}\n// console.log(maxSubArray([-2, 1, -3, 4, -1, 2, 1, -5, 4]))\n",
"url": "https://leetcode.cn/problems/maximum-subarray/description/",
"sortKey": 32
},
{
"name": "33.判断子序列.ts",
"type": "file",
"path": "src/8.动态规划/33.判断子序列.ts",
"content": "/**\n * @url https://leetcode.cn/problems/is-subsequence/description/\n */\n\n// TODO:双指针来判断\n// TODO:动态规划\n// abc\n// abcdef\n// dp[i][j]表示以s[i-1]为结尾,t[j-1]为结尾最长公共子序列\n// s[i-1]===t[j-1]?dp[i-1][j-1]+1:dp[i][j-1] ac addddc\n// notice:删除s的话一定是比删除t匹配结果更少的(这里也可以理解为剪枝的操作)\n// 结果比较最长公共子序列的长度是否和s的长度相等\nfunction isSubsequence(s: string, t: string): boolean {\n if (s.length < 1) {\n return true;\n }\n const dp = new Array(s.length + 1)\n .fill(0)\n .map((_item) => new Array(t.length + 1).fill(0));\n dp[0][0] = 0;\n dp[1][0] = 0;\n dp[0][1] = 0;\n for (let i = 1; i < s.length + 1; i++) {\n for (let j = 1; j < t.length + 1; j++) {\n if (s[i - 1] === t[j - 1]) {\n dp[i][j] = dp[i - 1][j - 1] + 1;\n } else {\n dp[i][j] = Math.max(dp[i][j - 1], dp[i - 1][j]);\n }\n }\n }\n return dp[s.length][t.length] === s.length;\n}\n",
"url": "https://leetcode.cn/problems/is-subsequence/description/",
"sortKey": 33
},
{
"name": "34.不同的子序列.ts",
"type": "file",
"path": "src/8.动态规划/34.不同的子序列.ts",
"content": "/**\n * @url https://leetcode.cn/problems/distinct-subsequences/\n */\n\n// s的子序列中t出现的个数 baggg bag\n// notice:思考:为啥不是累加,而是前一次的和再加入的?\n// dp[i][j] 以s[i-1],t[j-1]为结尾的t出现的个数 dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j]\n// 每一次状态 + 上一次未比较的状态 可以举一个t的长度为1的例子\nfunction numDistinct(s: string, t: string): number {\n const dp = new Array(s.length + 1).fill(0).map((_v) => new Array(t.length + 1).fill(0))\n for (let i = 0; i <= s.length; i++) {\n dp[i][0] = 1\n }\n for (let i = 1; i < s.length + 1; i++) {\n for (let j = 1; j < t.length + 1; j++) {\n if (s[i - 1] === t[j - 1]) {\n dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j]\n } else {\n dp[i][j] = dp[i - 1][j]\n }\n }\n }\n\n console.table(dp)\n\n return dp[s.length][t.length]\n}\n\nnumDistinct(\"babgbag\", \"bag\")\n",
"url": "https://leetcode.cn/problems/distinct-subsequences/",
"sortKey": 34
},
{
"name": "35.两个字符串的删除操作.ts",
"type": "file",
"path": "src/8.动态规划/35.两个字符串的删除操作.ts",
"content": "/**\n * @url https://leetcode.cn/problems/delete-operation-for-two-strings/description/\n */\nexport {}\n// aab aac\n// dp[i][j]表示以word1[i-1]为结尾和以word2[i-1]为结尾的最长公共子序列的长度\n// 相等 dp[i][j] = dp[i-1][j-1]+1\n// 不相等的话:dp[i][j] = max~dp[i-1][j],dp[i][j-1]\nfunction minDistance(word1: string, word2: string): number {\n const dp = new Array(word1.length + 1).fill(0).map((_v) => new Array(word2.length + 1).fill(0))\n\n for (let i = 1; i <= word1.length; i++) {\n for (let j = 1; j <= word2.length; j++) {\n if (word1[i - 1] === word2[j - 1]) {\n dp[i][j] = dp[i - 1][j - 1] + 1\n } else {\n dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1])\n }\n }\n }\n console.table(dp)\n\n return (\n Math.abs(dp[word1.length][word2.length] - word1.length) +\n Math.abs(dp[word1.length][word2.length] - word2.length)\n )\n}\n",
"url": "https://leetcode.cn/problems/delete-operation-for-two-strings/description/",
"sortKey": 35
},
{
"name": "36.编辑距离.ts",
"type": "file",
"path": "src/8.动态规划/36.编辑距离.ts",
"content": "/**\n * @url https://leetcode.cn/problems/edit-distance/description/\n */\n\nexport {}\n// function minDistance(word1: string, word2: string): number {\n// const dp = new Array(word1.length + 1).fill(0).map((_v) => new Array(word2.length + 1).fill(0))\n\n// for (let i = 1; i <= word1.length; i++) {\n// for (let j = 1; j <= word2.length; j++) {\n// if (word1[i - 1] === word2[j - 1]) {\n// dp[i][j] = dp[i - 1][j - 1] + 1\n// } else {\n// dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1])\n// }\n// }\n// }\n// const max = dp[word1.length][word2.length]\n\n// if (word1.length > word2.length) {\n// return Math.abs(word1.length - max)\n// } else {\n// return Math.abs(word2.length - max)\n// }\n// }\n// notice:不要想着操作都为替换,注意子序列中间的相对顺序\n// intention\n// execution\n\n// console.log(minDistance(\"intention\", \"execution\"))\n\n// TODO:最长重复子数组试试,不行还是是相对顺序的原因\n\n// TODO:需要的最少操作\n// dp[0][0]= 0\nfunction minDistance(word1: string, word2: string): number {\n const dp = new Array(word1.length + 1).fill(0).map((_v) => new Array(word2.length + 1).fill(0))\n for (let i = 0; i <= word1.length; i++) {\n dp[i][0] = i\n }\n for (let j = 0; j <= word2.length; j++) {\n dp[0][j] = j\n }\n for (let i = 1; i <= word1.length; i++) {\n for (let j = 1; j <= word2.length; j++) {\n if (word1[i - 1] === word2[j - 1]) {\n dp[i][j] = dp[i - 1][j - 1]\n } else {\n dp[i][j] = Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1, dp[i - 1][j - 1] + 1)\n }\n }\n }\n return dp[word1.length][word2.length]\n}\n",
"url": "https://leetcode.cn/problems/edit-distance/description/",
"sortKey": 36
},
{
"name": "37.回文子串.ts",
"type": "file",
"path": "src/8.动态规划/37.回文子串.ts",
"content": "/**\n * @url https://leetcode.cn/problems/palindromic-substrings/description/\n */\n// 示例 1:\n\n// 输入:s = \"abc\"\n// 输出:3\n// 解释:三个回文子串: \"a\", \"b\", \"c\"\n// 示例 2:\n\n// 输入:s = \"aaa\"\n// 输出:6\n// 解释:6个回文子串: \"a\", \"a\", \"a\", \"aa\", \"aa\", \"aaa\"\n\n// dp[i][j]表示以区间[i,j]的子串是否是回文字符串\n// s[i] === s[j]? dp[i][j] = dp[i+1][j-1]\n// abba a(1) ab(2) abb(3) abba(5)\nfunction countSubstrings(s: string): number {\n const dp = new Array(s.length).fill(true).map((_v) => new Array(s.length).fill(false))\n let res = 0\n\n // notice:根据递推公式来确认遍历顺序,从下网上,从左往右\n for (let i = s.length - 1; i >= 0; i--) {\n for (let j = i; j < s.length; j++) {\n if (s[i] === s[j]) {\n if (j - i <= 1) {\n dp[i][j] = true\n res++\n } else if (dp[i + 1][j - 1]) {\n dp[i][j] = true\n res++\n }\n }\n }\n }\n return res\n}\n",
"url": "https://leetcode.cn/problems/palindromic-substrings/description/",
"sortKey": 37
},
{
"name": "38.最长回文子序列.ts",
"type": "file",
"path": "src/8.动态规划/38.最长回文子序列.ts",
"content": "/**\n * @url https://leetcode.cn/problems/longest-palindromic-subsequence/description/\n */\n// dp[i][j] 区间[i,j]之间的最长回文子序列 s[i]===s[j]? dp[i+1][j-1]+2: dp[i+1][j] dp[i][j-1]\nfunction longestPalindromeSubseq(s: string): number {\n const dp = new Array(s.length).fill(0).map((_v) => new Array(s.length).fill(0))\n\n for (let i = 0; i < s.length; i++) {\n dp[i][i] = 1\n }\n\n for (let i = s.length - 1; i >= 0; i--) {\n for (let j = i + 1; j < s.length; j++) {\n if (s[i] === s[j]) {\n dp[i][j] = dp[i + 1][j - 1] + 2\n } else {\n dp[i][j] = Math.max(dp[i + 1][j], dp[i][j - 1])\n }\n }\n }\n return dp[0][s.length - 1]\n}\nlongestPalindromeSubseq(\"bbbab\")\n// ABCBDAB\n// BDCABA\n",
"url": "https://leetcode.cn/problems/longest-palindromic-subsequence/description/",
"sortKey": 38
}
],
"sortKey": 8
},
{
"name": "9.贪心算法",
"type": "directory",
"path": "src/9.贪心算法",
"children": [
{
"name": "1.分发饼干.ts",
"type": "file",
"path": "src/9.贪心算法/1.分发饼干.ts",
"content": "/**\n * @url https://leetcode.cn/problems/assign-cookies/description/\n */\n\n// notice:孩子不变,饼干是分发的\n// function findContentChildren(g: number[], s: number[]): number {\n// const sort = (arr: number[]) => arr.sort((a, b) => a - b)\n\n// let bottom = 0\n// sort(g)\n// sort(s)\n// for (let i = 0; i < Math.min(s.length, g.length); i++) {\n// if (s[bottom] >= g[i]) {\n// bottom++\n// }\n// }\n// return bottom\n// }\nfunction findContentChildren(g: number[], s: number[]): number {\n const sort = (arr: number[]) => arr.sort((a, b) => a - b)\n let child = 0\n sort(g)\n sort(s)\n for (let i = 0; i < s.length; i++) {\n if (s[i] >= g[child]) {\n child++\n }\n }\n\n return child\n}\n",
"url": "https://leetcode.cn/problems/assign-cookies/description/",
"sortKey": 1
},
{
"name": "2.摆动序列.ts",
"type": "file",
"path": "src/9.贪心算法/2.摆动序列.ts",
"content": "/**\n * @url https://leetcode.cn/problems/wiggle-subsequence/description/\n */\n// nums = [1,7,4,9,2,5]\n// 输出:6\n// 解释:整个序列均为摆动序列,各元素之间的差值为 (6, -3, 5, -7, 3) 。\n\n// dp[i]表示以nums[i]为结尾的摆动序列的长度\n// nums[i]-nums[i-1]>0 判断nums[i-1]-nums[i-2]<0 反之 dp[i] = dp[i-1]+1 dp[i] = dp[i-1]\n// notice:还是状态考虑错了,这样并没有考虑前面是山峰还是山谷 25/31\n// function wiggleMaxLength(nums: number[]): number {\n// const dp = new Array(nums.length).fill(0)\n// if (nums.length < 2) {\n// return 1\n// }\n// dp[0] = 1\n// dp[1] = nums[1] === nums[0] ? 1 : 2\n// if ((dp[1] - dp[0] > 0 && dp[2] - dp[1] < 0) || (dp[1] - dp[0] < 0 && dp[2] - dp[1] > 0)) {\n// dp[2] = 3\n// }\n// for (let i = 2; i < nums.length; i++) {\n// if (\n// (nums[i] - nums[i - 1] > 0 && nums[i - 1] - nums[i - 2] < 0) ||\n// (nums[i] - nums[i - 1] < 0 && nums[i - 1] - nums[i - 2] > 0)\n// ) {\n// dp[i] = dp[i - 1] + 1\n// } else {\n// dp[i] = dp[i - 1]\n// }\n// }\n// console.table(dp)\n// return Math.max(...dp)\n// }\n// wiggleMaxLength([84])\n\n// TODO:动态规划思路2\n// PS: 动态规划这里做会比较好理解\n// dp[i][0] = max(dp[i][0], dp[j][1] + 1),其中0 < j < i且nums[j] < nums[i],表示将 nums[i]接到前面某个山谷后面,作为山峰。\n// dp[i][1] = max(dp[i][1], dp[j][0] + 1),其中0 < j < i且nums[j] > nums[i],表示将 nums[i]接到前面某个山峰后面,作为山谷。\nfunction wiggleMaxLength(nums: number[]): number {\n const dp = new Array(nums.length).fill(0).map((_v) => new Array(2).fill(0));\n dp[0][0] = 1;\n dp[0][1] = 1;\n for (let i = 1; i < nums.length; i++) {\n // 这里的初始化:两个数相等,算是一个数的情况的摆动序列\n dp[i][0] = 1;\n dp[i][1] = 1;\n for (let j = 0; j < i; j++) {\n if (nums[i] - nums[j] > 0) {\n // 前面应该是谷底\n dp[i][1] = Math.max(dp[j][0] + 1, dp[i][1]);\n } else if (nums[i] - nums[j] < 0) {\n dp[i][0] = Math.max(dp[j][1] + 1, dp[i][0]);\n }\n }\n }\n return Math.max(...dp[nums.length - 1]);\n}\n",
"url": "https://leetcode.cn/problems/wiggle-subsequence/description/",
"sortKey": 2
},
{
"name": "3.最大子数组和.ts",
"type": "file",
"path": "src/9.贪心算法/3.最大子数组和.ts",
"content": "/**\n * @url https://leetcode.cn/problems/maximum-subarray/\n */\n\n// 输入:nums = [-2,1,-3,4,-1,2,1,-5,4]\n// 输出:6\n// 解释:连续子数组 [4,-1,2,1] 的和最大,为 6。\n\n// dp[i]表示以nums[i-1]为结尾的最大子数组和 dp[i] = dp[i-1]+nums[i]\n// function maxSubArray(nums: number[]): number {\n// const dp = new Array(nums.length + 1).fill(Number.MIN_SAFE_INTEGER)\n\n// for (let i = 1; i <= nums.length; i++) {\n// dp[i] = Math.max(dp[i - 1] + nums[i - 1], nums[i - 1])\n// }\n// console.table(dp)\n// return Math.max(...dp)\n// }\n\n// TODO:贪心做法\nfunction maxSubArray(nums: number[]): number {\n let count = 0, //统计目前累加的和\n result = Number.MIN_SAFE_INTEGER; // 放置结果\n for (let i = 0; i < nums.length; i++) {\n count += nums[i];\n if (count > result) {\n result = count;\n }\n if (count < 0) {\n count = 0;\n }\n }\n return result;\n}\n",