-
Notifications
You must be signed in to change notification settings - Fork 684
/
LeetCode1.md
2371 lines (2007 loc) · 87.8 KB
/
LeetCode1.md
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
## LeetCode 热门100题-题解(下)
##### 主要是记录自己刷题的过程,也方便自己复习
##### [第155题-最小栈](#第155题-最小栈)
##### [第160题-相交链表](#第160题-相交链表)
##### [第142题-环形链表II](#第142题-环形链表II)
##### [第739题-每日温度](#第739题-每日温度)
##### [第347题-前K个高频元素](#第347题-前K个高频元素)
##### [第49题-字母异位词分组](#第49题-字母异位词分组)
##### [第32题-最长有效括号](#第32题-最长有效括号)
##### [第543题-二叉树的直径](#第543题-二叉树的直径)
##### [第79题-单词搜索](#第79题-单词搜索)
##### [第96题-不同的二叉搜索树](#第96题-不同的二叉搜索树)
##### [第239题-滑动窗口最大值](#第239题-滑动窗口最大值)
##### [第146题-LRU缓存机制](#第146题-LRU缓存机制)
##### [第236题-二叉树的最近公共祖先](#第236题-二叉树的最近公共祖先)
##### [第114题-二叉树展开为链表](#第114题-二叉树展开为链表)
##### [第84题-柱状图中最大的矩形](#第84题-柱状图中最大的矩形)
##### [第148题-排序链表](#第148题-排序链表)
##### [第617题-合并二叉树](#第617题-合并二叉树)
##### [第287题-寻找重复数](#第287题-寻找重复数)
##### [第152题-乘积最大子数组](#第152题-乘积最大子数组)
##### [第72题-编辑距离](#第72题-编辑距离)
##### [第139题-单词拆分](#第139题-单词拆分)
##### [第76题-最小覆盖子串](#第76题-最小覆盖子串)
##### [第124题-二叉树中的最大路径和](#第124题-二叉树中的最大路径和)
##### [第461题-汉明距离](#第461题-汉明距离)
##### [第128题-最长连续序列](#第128题-最长连续序列)
##### [第647题-回文子串](#第647题-回文子串)
##### [第337题-打家劫舍III](#第337题-打家劫舍III)
##### [第238题-除自身以外数组的乘积](#第238题-除自身以外数组的乘积)
##### [第207题-课程表](#第207题-课程表)
##### [第309题-最佳买卖股票时机含冷冻期](#第309题-最佳买卖股票时机含冷冻期)
##### [第416题-分割等和子集](#第416题-分割等和子集)
##### [第560题-和为K的子数组](#第560题-和为K的子数组)
##### [第448题-找到所有数组中消失的数字](#第448题-找到所有数组中消失的数字)
##### [第437题-路径总和III](#第437题-路径总和III)
##### [第338题-比特位计数](#第338题-比特位计数)
##### [第406题-根据身高重建队列](#第406题-根据身高重建队列)
##### [第538题-把二叉搜索树转换为累加树](#第538题-把二叉搜索树转换为累加树)
##### [第297题-二叉树的序列化与反序列化](#第297题-二叉树的序列化与反序列化)
##### [第438题-找到字符串中所有字母异位词](#第438题-找到字符串中所有字母异位词)
##### [第240题-搜索二维矩阵II](#第240题-搜索二维矩阵II)
##### [第494题-目标和](#第494题-目标和)
##### [第621题-任务调度器](#第621题-任务调度器)
##### [第581题-最短无序连续子数组](#第581题-最短无序连续子数组)
### 第155题-最小栈
设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。
push(x) —— 将元素 x 推入栈中。
pop() —— 删除栈顶的元素。
top() —— 获取栈顶元素。
getMin() —— 检索栈中的最小元素。
示例:
输入:
["MinStack","push","push","push","getMin","pop","top","getMin"]
[[],[-2],[0],[-3],[],[],[],[]]
输出:
[null,null,null,null,-3,null,0,-2]
解释:
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin(); --> 返回 -3.
minStack.pop();
minStack.top(); --> 返回 0.
minStack.getMin(); --> 返回 -2.
##### 解题思路
就是用一个最小栈来记录最小值
```java
Stack<Integer> stack;
Stack<Integer> minStack;
/** initialize your data structure here. */
public MinStack() {
stack = new Stack<Integer>();
minStack = new Stack<Integer>();
}
public void push(int x) {
if(minStack.size()==0||x<=minStack.peek()) {
minStack.push(x);
}
stack.push(x);
}
public void pop() {
if(stack.peek().equals(minStack.peek())) {
minStack.pop();
}
stack.pop();
}
public int top() {
return stack.peek();
}
public int getMin() {
return minStack.peek();
}
```
### 第160题-相交链表
编写一个程序,找到两个单链表相交的起始节点。
如下面的两个链表**:**
[![img](../static/160_statement.png)](https://assets.leetcode-cn.com/aliyun-lc-upload/uploads/2018/12/14/160_statement.png)
在节点 c1 开始相交。
##### 解题思路
分别计算出链表A,链表B的长度,让长的链表先走n步,直到两个链表剩余节点长度一样,然后每个链表都走一步,直到节点相等,即为相交节点。
```java
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
int lengthA=0;
ListNode nodeA = headA;
while(nodeA!=null) {
lengthA++;
nodeA=nodeA.next;
}
int lengthB=0;
ListNode nodeB = headB;
while(nodeB!=null) {
lengthB++;
nodeB=nodeB.next;
}
nodeA = headA;
nodeB = headB;
while(lengthA!=lengthB) {
if (lengthA>lengthB) {
lengthA--;
nodeA = nodeA.next;
} else {
lengthB--;
nodeB = nodeB.next;
}
}
while(nodeA!=null && nodeB!=null) {
if(nodeA == nodeB) {
return nodeA;
}
nodeA = nodeA.next;
nodeB = nodeB.next;
}
return null;
}
```
### 第142题-环形链表II
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意,pos 仅仅是用于标识环的情况,并不会作为参数传递到函数中。
说明:不允许修改给定的链表。
进阶:
你是否可以使用 O(1) 空间解决此题?
示例 1:
![img](../static/circularlinkedlist-9062165.png)
输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。
##### 解题思路
```java
public ListNode detectCycle(ListNode head) {
//快慢节点在圆中相遇
//然后慢节点在圆中走一圈,计算出圆的周长
//快节点凑头
if (head==null || head.next == null) {
return null;
}
ListNode slow = head.next;
ListNode quick = head.next.next;
//通过快慢指针,让两个指针在环中相遇
while (quick!=slow) {
if (quick == slow && quick!=null) {
break;
}
slow = slow.next;
if (quick==null||quick.next == null) {
return null;
}
quick = quick.next;
quick = quick.next;
}
//计算环的长度
int circleLength = 1;
ListNode copySlow = slow.next;
while (copySlow != slow) {
copySlow = copySlow.next;
circleLength++;
}
//快指针先走环的长度步
quick = head;
while (circleLength>0) {
quick = quick.next;
circleLength--;
}
slow = head;
//慢指针出发,相遇的节点就是环的入口
while (quick!=slow) {
quick = quick.next;
slow = slow.next;
}
return quick;
}
```
### 第739题-每日温度
请根据每日 气温 列表,重新生成一个列表。对应位置的输出为:要想观测到更高的气温,至少需要等待的天数。如果气温在这之后都不会升高,请在该位置用 0 来代替。
例如,给定一个列表 temperatures = [73, 74, 75, 71, 69, 72, 76, 73],你的输出应该是 [1, 1, 4, 2, 1, 1, 0, 0]。
提示:气温 列表长度的范围是 [1, 30000]。每个气温的值的均为华氏度,都是在 [30, 100] 范围内的整数。
##### 解题思路
其实跟滑动窗口最大值那个题的解题思路很像,就是维护一个还没有排序好的栈,每次把栈中比当前遍历元素小的,都出栈,然后计算等待天数,然后将当前元素入栈。
```java
public int[] dailyTemperatures(int[] T) {
if (T == null || T.length==0) {
return null;
}
int[] result = new int[T.length];
Stack<Integer> stack = new Stack<>();
stack.add(0);
for (int i = 1; i < T.length; i++) {
//比栈顶元素小,栈
if (stack.size() == 0 || T[i] <= T[stack.peek()]) {
stack.push(i);
} else {
// 比栈顶元素大,就一直出栈
while (stack.size() >0 && T[i] > T[stack.peek()]) {
int index = stack.pop();
result[index] = i - index;
}
stack.push(i);
}
}
return result;
}
```
### 第347题-前K个高频元素
给定一个非空的整数数组,返回其中出现频率前 k 高的元素。
示例 1:
输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]
示例 2:
输入: nums = [1], k = 1
输出: [1]
##### 解题思路
先使用一个HashMap来统计各元素的频率,然后取前k个元素建立小顶堆,然后对后面的元素进行遍历,如果频率高于堆顶元素,就与堆顶元素交换,然后对堆进行调整。
```java
public int[] topKFrequent(int[] nums, int k) {
HashMap<Integer,Integer> map = new HashMap<Integer,Integer>();
for(int i = 0;i<nums.length;i++) {
Integer value = map.get(nums[i]);
value = value == null ? 1 : value+1;
map.put(nums[i],value);
}
Object[] array = map.keySet().toArray();
//进行堆排
int[] result = new int[k];
for (int i = 0; i < k; i++) {
result[i] = (Integer)array[i];
}
//建立小顶堆
for (int i = result.length/2-1; i >= 0; i--) {
adjustHeap(result,i,result.length,map);
}
for (int i = k; i < array.length ; i++) {
Integer key = (Integer) array[i];
if (map.get(key) >= map.get(result[0])) {
result[0] = (Integer)array[i];
adjustHeap(result,0,result.length,map);
}
}
return result;
}
void adjustHeap(int[] array, int i,int length,HashMap<Integer,Integer> map) {
while (2*i+1<length) {//左节点存在
int left = 2*i+1;
int right = 2*i+2;
int min = map.get(array[i]) < map.get(array[left]) ? i : left;
if (right<length && map.get(array[right]) < map.get(array[min])) {//右节点存在,并且还比最大值大
min = right;
}
if (min == i) {//当前已经是最小值
break;
} else {//左节点或者右节点是最小值,那么就将当前节点与最小的节点交换
swap(array,i,min);
i = min;
}
}
}
void swap(int[] array, int a,int b) {
int temp = array[a];
array[a] = array[b];
array[b] = temp;
}
```
### 第49题-字母异位词分组
给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串。
示例:
输入: ["eat", "tea", "tan", "ate", "nat", "bat"]
输出:
[
["ate","eat","tea"],
["nat","tan"],
["bat"]
]
说明:
所有输入均为小写字母。
不考虑答案输出的顺序。
##### 解题思路
就是对字符串进行遍历,取出每个字符串,计算它对应的字典序最小的字符串(例如:"ate","eat","tea",他们字典序最小的字符串是"aet"),然后判断map中是否存在,不存在就创建一个List,将字符串添加进去,存在就在原List中添加该字符串。
```java
public List<List<String>> groupAnagrams(String[] strs) {
HashMap<String,List> map = new HashMap<String,List>();
for (String str : strs) {
char[] charArray = str.toCharArray();
Arrays.sort(charArray);
String sortedString = new String(charArray);
List strList = map.get(sortedString);
if (strList == null) {
strList = new LinkedList();
}
strList.add(str);
map.put(sortedString,strList);
}
ArrayList totalList = new ArrayList<>();
totalList.addAll(map.values());
return totalList;
}
```
### 第32题-最长有效括号
给定一个只包含 '(' 和 ')' 的字符串,找出最长的包含有效括号的子串的长度。
示例 1:
输入: "(()"
输出: 2
解释: 最长有效括号子串为 "()"
示例 2:
输入: ")()())"
输出: 4
解释: 最长有效括号子串为 "()()"
##### 解题思路
判断一个字符是否在是有效括号,就是对字符串遍历,如果是(,就将(字符入栈,如果是)字符,就判断栈是否为有元素,有元素代表括号匹配上了,将当前括号和栈顶元素括号都标记为有效括号。标记的方式是将recordArray数组中相应下标置为1,然后统计最长有效括号就是统计recordArray数组中连续1的个数。
```java
public int longestValidParentheses(String s) {
Stack<Integer> stack = new Stack();
int[] recordArray = new int[s.length()];
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c=='(') {
stack.push(i);
} else if (c==')' && stack.size()>0) {
//说明这个位置可以匹配
recordArray[i]=1;
int leftIndex = stack.pop();
recordArray[leftIndex] = 1;
}
}
//统计recordArray张连续1的长度
int max = 0;
int currentSize =0;
for (int i = 0; i < recordArray.length; i++) {
if (recordArray[i] ==0) {
currentSize=0;
} else if (recordArray[i] ==1) {
currentSize++;
}
max = max > currentSize ? max : currentSize;
}
return max;
}
```
### 第543题-二叉树的直径
给定一棵二叉树,你需要计算它的直径长度。一棵二叉树的直径长度是任意两个结点路径长度中的最大值。这条路径可能穿过也可能不穿过根结点。
示例 :
给定二叉树
1
/ \
2 3
/ \
4 5
返回 3, 它的长度是路径 [4,2,1,3] 或者 [5,2,1,3]。
##### 解题思路
其实根节点的直径就是左节点深度+右节点深度,其他节点也是这个公示,所以可以递归求解出左右节点的深度,然后计算当前节点的直径,然后与左右节点的直径进行判断,返回最大值。
```java
int maxDiameter = 0;
public int diameterOfBinaryTree(TreeNode root) {
if (root==null){return 0;}
maxDepth(root);
return maxDiameter;
}
public int maxDepth(TreeNode root) {
if (root==null) {return 0;}
int leftDepth = maxDepth(root.left);
int rightDepth = maxDepth(root.right);
int diameter = leftDepth + rightDepth;
maxDiameter = diameter > maxDiameter ? diameter : maxDiameter;
return leftDepth>rightDepth?leftDepth+1 : rightDepth+1;
}
```
### 第79题-单词搜索
给定一个二维网格和一个单词,找出该单词是否存在于网格中。
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
示例:
board =
[
['A','B','C','E'],
['S','F','C','S'],
['A','D','E','E']
]
给定 word = "ABCCED", 返回 true
给定 word = "SEE", 返回 true
给定 word = "ABCB", 返回 false
##### 解题思路
就是遍历数组,判断每个字符与字符串首字符是否相同,相同的话就继续判断周围的字符是否满足要求。需要注意的时,在判断时会把走过的路径添加到HashSet中去,防止一个位置的字符重复被使用。
```java
HashSet<String> set = new HashSet<>();
public boolean exist(char[][] board, String word) {
if (board==null||board[0]==null||word==null||word.length()==0) {
return false;
}
char firstChar = word.charAt(0);
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board[0].length; j++) {
set = new HashSet<>();
if(board[i][j] == firstChar
&& judgeTheChar(board, word,1,i,j) == true) {
return true;
}
}
}
return false;
}
boolean judgeTheChar(char[][] board,String word,int index,int i,int j) {
String key = i + "-" + j;
if (set.contains(key)) {
return false;
} else {
set.add(key);
}
if (index>= word.length()) {
return true;
}
char currentChar = word.charAt(index);
//上
if (i-1>=0 &&
board[i-1][j] == currentChar && judgeTheChar(board, word,index+1,i-1,j) == true) {
return true;
}
//下
if (i+1<board.length &&
board[i+1][j] == currentChar
&& judgeTheChar(board, word,index+1,i+1,j) == true) {
return true;
}
//左
if (j-1>=0 &&
board[i][j-1] == currentChar
&& judgeTheChar(board, word,index+1,i,j-1) == true) {
return true;
}
if (j+1< board[0].length &&
board[i][j+1] == currentChar
&& judgeTheChar(board, word,index+1,i,j+1) == true) {
return true;
}
return false;
}
```
### 第96题-不同的二叉搜索树
给定一个整数 n,求以 1 ... n 为节点组成的二叉搜索树有多少种?
示例:
输入: 3
输出: 5
解释:
给定 n = 3, 一共有 5 种不同结构的二叉搜索树:
1 3 3 2 1
\ / / / \ \
3 2 1 1 3 2
/ / \ \
2 1 2 3
##### 解题思路
就是把一个二叉搜索树的组合种数,其实是左子树的组合数乘以右子树的组合数,假设n为4,f(n)代表组合种数,二叉树共有四个节点,那么二叉树肯定有根节点,组合主要分为以下几类:
1.左子树有0个节点,右子树有3个节点,f(0)*f(3)
2.左子树有1个节点,右子树有2个节点,f(1)*f(2)
3.左子树有2个节点,右子树有1个节点,f(2)*f(1)
4.左子树有3个节点,右子树有0个节点,f(3)*f(0)
所以,f(4)=f(0)*f(3)+f(1)*f(2)+f(2)*f(1)+f(3)*f(0)
而f(0)=1,f(1)=1,f(2)=2
```java
public int numTrees(int n) {
int[] array = new int[n+1];
array[0] = 1;
for (int i = 1; i <= n; i++) {
int num = 0;
for (int j = 0; j <=i-1; j++) {
num += array[j]*array[i-1-j];
}
array[i] = num;
}
return array[n];
}
```
递归解法
```java
int[] cacheArray;
public int numTrees(int n) {
if(n == 0 || n == 1){return 1;}
if(n==2) {
return 2;
}
if(cacheArray == null) {
cacheArray = new int[n+1];
} else if (cacheArray[n] !=0) {
return cacheArray[n];
}
int sum = 0;
for(int i = 0; i<n;i++) {
sum+=numTrees(i)*numTrees(n-1-i);
}
cacheArray[n] = sum;
return sum;
}
```
### 第239题-滑动窗口最大值
给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
返回滑动窗口中的最大值。
示例 1:
输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]
解释:
滑动窗口的位置 最大值
--------------- -----
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7
##### 解题思路
其实是可以维护一个已排序好的队列,每次将刚进窗口的值,添加到排序好的队列中,像插入排序的原理一样,然后取最大值的时候就去队列头部取,然后如果是超出窗口左边界的,就丢掉,没有超过就使用。但是主要问题在于每次插入时的平均时间复杂度是k/2,总时间复杂度就是Nk/2,当k接近于n时,这个复杂度就是O(N^2)了,所以在往队列中插入时,要有排除机制,假设队列是[6,5,4,2]四个数,此时要插入的元素是3,那么在插入时可以把2直接丢掉,因为元素2是3之前的元素,下标更小,值也比3小,在后面的遍历过程中是不可能再成为最大值的,所以通过把2删除掉,这样就可以减少插入时,比次数,将时间复杂度降低为O(N).
```java
public int[] maxSlidingWindow(int[] nums, int k) {
int[] result = new int[nums.length-k+1];
LinkedList<Integer> maxQueue = new LinkedList<>();
for (int i = 0; i < nums.length; i++) {
//将队列中小于当前数的元素出队列
while (maxQueue.size()>0 && nums[i] > nums[maxQueue.getLast()]) {
maxQueue.removeLast();
}
maxQueue.add(i);
//窗口的左边界
int left = i - k+1;
//将队列中出边界的最大值移除
if (maxQueue.getFirst() <left) {
maxQueue.removeFirst();
}
//左边界全部到数组中时,才需要计算最大值
if (left>=0) {
result[left] = nums[maxQueue.getFirst()];
}
}
return result;
}
```
### 第146题-LRU缓存机制
运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制 。
实现 LRUCache 类:
LRUCache(int capacity) 以正整数作为容量 capacity 初始化 LRU 缓存
int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
void put(int key, int value) 如果关键字已经存在,则变更其数据值;如果关键字不存在,则插入该组「关键字-值」。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。
##### 解题思路
LinkedHashMap本身是基于HashMap做了一些扩展,就是通过链表将所有键值对连接起来了,链接的就是添加键值对的顺序。(新添加的键值对在链表尾部,put方法对于已存在的key进行值覆盖时,是不会修改键值对在链表中的顺序的)
所以我们可以基于LinkedHashMap实现LRU。
LRU的get()方法
1.map中不存在这个key,返回-1
2.map中存在这个key,先remove这个键值对,再put操作添加这个键值对,再返回value。(这样可以修改键值对在链表中的顺序。)
LRU的put()方法
1.已存在这个key,先remove,再put。
2.不存在这个key,判断是否超过容量,超过将最后一个键值对移除,将键值对添加到map。
```java
LinkedHashMap<Integer, Integer> map = new LinkedHashMap();
int capacity;
public LRUCache(int capacity) {
this.capacity = capacity;
}
public int get(int key) {
Integer value = map.get(key);
if (value == null) {
return -1;
}
map.remove(key);
map.put(key,value);
return value;
}
public void put(int key, int value) {
Integer oldValue = map.get(key);
if (oldValue!=null) {
//只是覆盖value的话,put方法不会改变键值对在链表中的顺序,所以需要先remove
map.remove(key);
map.put(key,value);
return;
}
if (map.size()>=capacity) {
map.remove(map.keySet().iterator().next());
}
map.put(key,value);
}
```
### 第236题-二叉树的最近公共祖先
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
例如,给定如下二叉树: root = [3,5,1,6,2,0,8,null,null,7,4]
示例 1:
输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出: 3
解释: 节点 5 和节点 1 的最近公共祖先是节点 3。
示例 2:
输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出: 5
解释: 节点 5 和节点 4 的最近公共祖先是节点 5。因为根据定义最近公共祖先节点可以为节点本身。
##### 解题思路
其实所有的节点分为以下几种:
1.就是要寻找的节点1,或节点2
2.此节点子树中中包含节点1,节点2其中的一个
3.节点1,节点2全部位于此节点的左子树,或者是右子树
4.此节点左子树包含节点1,右子树包含节点2
所以第4种就是我们要寻找的节点,并且在二叉树中只有一个,所以我们对二叉树进行遍历,判断某个节点左子树,右子树都包含节点,那么就返回该节点。
```java
TreeNode lowestCommonAncestor(TreeNode root, TreeNode node1, TreeNode node2) {
if (root==null) {
return null;
}
if (root==node1 || root==node2) {//当前节点就是要找的节点之一
return root;
}
TreeNode leftNode = lowestCommonAncestor(root.left,node1,node2);//判断左子树中是否有节点
TreeNode rightNode = lowestCommonAncestor(root.right,node1,node2);//判断右子树中是否有节点
if (leftNode!=null&&rightNode!=null) {//就是我们要找的节点
return root;
} else if (leftNode!=null && rightNode==null) {//左子树中有节点,右子树没有节点,继续向上遍历
return leftNode;
} else if (leftNode==null && rightNode!=null) {//继续向上遍历
return rightNode;
}
return null;
}
```
### 第114题-二叉树展开为链表
给定一个二叉树,原地将它展开为一个单链表。
例如,给定二叉树
1
/ \
2 5
/ \ \
3 4 6
将其展开为:
1
\
2
\
3
\
4
\
5
\
6
##### 解题思路
其实就是先序遍历,需要注意的是,需要先将节点的左右节点保存,然后再进行修改操作。其次是需要将所有节点的left指针置为null.
```java
TreeNode lastNode;//lastNode用于保存上一个遍历的节点
public void flatten(TreeNode root) {
if(root == null) {return;}
TreeNode left = root.left;
TreeNode right = root.right;
if(lastNode==null) {
lastNode = root;
lastNode.left=null;
} else {
lastNode.left=null;
lastNode.right = root;
lastNode=root;
}
flatten(left);
flatten(right);
}
```
### 第84题-柱状图中最大的矩形
给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
求在该柱状图中,能够勾勒出来的矩形的最大面积。
![img](../static/histogram.png)
以上是柱状图的示例,其中每个柱子的宽度为 1,给定的高度为 [2,1,5,6,2,3]。
![img](../static/histogram_area.png)
图中阴影部分为所能勾勒出的最大矩形面积,其面积为 10 个单位。
示例:
输入: [2,1,5,6,2,3]
输出: 10
##### 解题思路
可以暴力求解,就对每个height[i]都计算包含它的元素的最大高度,这样复杂度是O(N^2),计算的方法主要是对于每个i找到左边最后一个大于height[i]的边界left,和右边最后一个大于height[i]的边界right,
矩形面积=height[i]*(right-left+1),例如对于第五个柱子值为2的那个柱子来说,左边界left就是值为5的柱子,右边界就是值为3的柱子。
单调栈解法
理论就是假设f(i)代表包含圆柱i的最大矩形,那么其实就是在圆柱i的左边和右边各自找到第一个高度更小的圆柱k和j,f(i) = height[i]*(j-k+1)。所以可以使用单调栈来保存比当前圆柱高度height[i]小的元素,如果栈中元素高度比height[i]大,那么那些元素就需要出栈了。
可以使用一个栈Stack来保存左边圆柱高度比height[i]小的元素,也就是Stack中的值只能是比height[i]小的元素。栈底元素是最小的,栈顶元素高度是最大的。
遍历时,如果当前height[i]>栈顶元素的高度,说明当前栈顶元素还没有碰到比它小的数,所以还不能计算面积,就把height[i]入栈。
如果现在的height[i]<栈顶的元素高度小,说明栈顶元素碰到比它小的元素了,就需要出栈计算矩形面积,
面积=heights[j] * (i - left)
```java
public int largestRectangleArea(int[] heights) {
if (heights==null||heights.length==0) {
return 0;
}
int maxArea = 0;
Stack<Integer> stack = new Stack();
stack.add(0);
for (int i = 1; i <= heights.length; i++) {
int currentH;
if (i==heights.length) {
//这是最后新增了一个0元素
//防止整个数组是全体递增的,这样会计算不出面积
currentH = 0;
} else {
currentH = heights[i];
}
if(currentH > heights[stack.peek()]) {
stack.push(i);
} else if (currentH == heights[stack.peek()]) {
stack.pop();
stack.push(i);
} else {//当heights[i] < stack.peek()时,进行出栈计算
while (stack.size()>0 && currentH<heights[stack.peek()]) {
Integer index = stack.pop();
//此时area的范围是[stack.peek()+1,i-1]
int leftIndex;
if (stack.size()>0) {
leftIndex = stack.peek() + 1;
} else {
leftIndex = 0;
}
int area = heights[index] * (i - leftIndex);
maxArea = maxArea > area ? maxArea : area;
}
stack.push(i);
}
}
return maxArea;
}
```
### 第148题-排序链表
给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。
进阶:
你可以在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序吗?
示例 1:
![img](../static/sort_list_1.jpg)
输入:head = [4,2,1,3]
输出:[1,2,3,4]
##### 解题思路
就是归并排序
```java
ListNode preHead = new ListNode(1);
ListNode sortList(ListNode start) {
//归并排序划分到只有一个节点时,直接返回
if (start.next == null || start==null) {return start;}
ListNode quick = start;
ListNode slow = start;
while (quick!=null) {
quick=quick.next;
if (quick.next==null){break;}
quick = quick.next;
slow = slow.next;
}
//4 2 1 3
ListNode otherStart = slow.next;
slow.next = null;
//对链表分解和合并后,新链表的头结点不一定还是start
start = sortList(start);
otherStart = sortList(otherStart);
//开始merge
//preHead用于我们来保存新组成的链表的头结点
ListNode currentNode = preHead;
while (start != null && otherStart!=null) {
if (start.val<otherStart.val) {
currentNode.next = start;
currentNode=currentNode.next;
start=start.next;
} else {
currentNode.next = otherStart;
currentNode=currentNode.next;
otherStart=otherStart.next;
}
}
while (start!=null) {
currentNode.next = start;
currentNode=currentNode.next;
start=start.next;
}
while (otherStart!=null) {
currentNode.next = otherStart;
currentNode=currentNode.next;
otherStart=otherStart.next;
}
return preHead.next;
}
```
### 第617题-合并二叉树
给定两个二叉树,想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些节点便会重叠。
你需要将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,否则不为 NULL 的节点将直接作为新二叉树的节点。
示例 1:
输入:
Tree 1 Tree 2
1 2
/ \ / \
3 2 1 3
/ \ \
5 4 7
输出:
合并后的树:
3
/ \
4 5
/ \ \
5 4 7
##### 解题思路
```
public TreeNode mergeTrees(TreeNode t1, TreeNode t2) {
if(t1==null || t2==null) {
return t1==null? t2 : t1;
}
t1.val = t1.val+t2.val;
t1.left = mergeTrees(t1.left,t2.left);
t1.right = mergeTrees(t1.right,t2.right);
return t1;
}
```
### 第287题-寻找重复数
给定一个包含 n + 1 个整数的数组 nums,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数。假设只有一个重复的整数,找出这个重复的数。
示例 1:
输入: [1,3,4,2,2]
输出: 2
示例 2:
输入: [3,1,3,4,2]
输出: 3
说明:
不能更改原数组(假设数组是只读的)。
只能使用额外的 O(1) 的空间。
时间复杂度小于 O(n2) 。
数组中只有一个重复的数字,但它可能不止重复出现一次。
##### 解题思路
这个题主要是不能使用额外的辅助空间,不然可以使用一个数组或者HashMap记录遍历过程中出现的整数,然后判断当前数是否重复。本题只能使用数组本身来记录整数是否出现,所以可以将每个数nums[i]交换到以nums[i]作为下标的地方,如果此时以存在相同的数,说明重复,否则就对交换后的数继续遍历。