-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathleetcode.cpp
More file actions
1939 lines (1701 loc) · 57.1 KB
/
Copy pathleetcode.cpp
File metadata and controls
1939 lines (1701 loc) · 57.1 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
/**
* date: 2018-6-24
* author: zlm
* Leetcode top 100 && nowcoder && jianzhioffer
*/
#include <iostream>
#include <cmath>
#include <string>
#include <cstring>
#include <queue>
#include <deque>
#include <stack>
#include <numeric>
#include <algorithm>
#include <vector>
#include <map>
#include <set>
#include <unordered_map>
#include <climits>
#include <utility>
#include <functional>
using namespace std;
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
struct ListNode {
int val;
ListNode *next;
ListNode(int x) : val(x), next(NULL) {}
};
// 1.Two Sum
class Solution {
public:
vector<int> twoSum(vector<int> &numbers, int target) {
if (numbers.empty())
return vector<int>();
vector<int> indice;
map<int, int> hash;
int len = numbers.size();
for (int i = 0; i < len; i++) {
hash[numbers[i]] = i;
}
for (int i = 0; i < len; i++) {
int sub = target - numbers[i];
// 题目要求满足条件的两个数必须不同,故不需要考虑同一个元素加上自身的情况
if (hash.find(sub) != hash.end() && hash[sub] > i) {
indice.push_back(i + 1);
indice.push_back(hash[sub] + 1);
break;
}
}
return indice;
}
};
// improved version
vector<int> twoSum2(vector<int> &num, int target) {
if (num.empty())
return vector<int>();
vector<int> res(2);
unordered_map<int, int> mp;
int len = num.size();
for (int i = 0; i < len; i++) {
int sub = target - num[i];
if (mp.find(sub) != mp.end()) { // 已经找到了两个元素
res[0] = mp[sub];
res[1] = mp[num[i]];
break;
}
else
mp.insert({num[i], i}); // mp[num[i]] = i;
}
}
// 2.reverse list
// 使用循环实现(no head node)
class Solution7 {
public:
ListNode* reverseList(ListNode* head) {
if (head == nullptr || head->next == nullptr) // 只有一个结点或者为空
return head;
//ListNode *newHead = new ListNode(0);
ListNode *pre = head;
ListNode *pcur = head->next;
ListNode *pnext = nullptr;
while (pcur != nullptr) {
pnext = pcur->next;
pcur->next = pre; // 改变指向
pre = pcur;
pcur = pnext;
} // end 此时pre指向最后一个结点,即反向后的第一个结点
head->next = nullptr;
return pre;
}
};
// 3.带环链表的入口结点
// fast and slow pointer
class Solution8 {
public:
ListNode *detectCycle(ListNode *head) {
if (head == nullptr || head->next == nullptr)
return nullptr;
ListNode *fast = head;
ListNode *slow = head;
while (fast != nullptr && fast->next != nullptr) {
fast = fast->next->next;
slow = slow->next;
if (slow == fast)
break;
}
// 正常退出while循环,说明没有环存在
if (fast == nullptr || fast->next == nullptr)
return nullptr;
// 否则:slow==fast,存在环,设置slow = head,当slow移动到入口结点,则fast正好移动至入口结点
slow = head;
while (slow != fast) {
slow = slow->next;
fast = fast->next;
}
return fast;
}
};
// 5.数组中第k大的元素
// 思路:把数据从大到小排列,从前向后遍历寻找第k大的元素
// 不建议这种方法,可通过小顶堆来实现
class Solution9 {
public:
int findKthLargest(vector<int>& nums, int k) {
if (k < 1 || k > (int)nums.size() || nums.empty())
return 0;
sort(nums.begin(), nums.end());
reverse(nums.begin(), nums.end()); // 把序列从大到小排列
int len = nums.size();
return nums[k - 1];
}
};
/** 题目 347. Top K Frequent Elements
* @author: zlm
* @date: 2018-6-25 10:40
* @description: 统计前k个最频繁出现的数字
*/
class Solution10 {
public:
vector<int> topKFrequent(vector<int>& nums, int k) {
vector<int> res;
if (nums.empty())
return res;
unordered_map<int, int> mp;
for (auto v : nums) {
mp[v]++;
}
if (k > mp.size())
return res;
priority_queue<pair<int, int>> que; // 使用优先队列(基于红黑树实现,队头元素top最大)
// 按元素出现频次进行排序
for (auto it = mp.begin(); it != mp.end(); it++) {
que.push({it->second, it->first });
}
// 队首存放的是次数最多的元素
while (k--) {
res.push_back(que.top().second);
que.pop();
}
return res;
}
};
/** 347. Top K Frequent Elements
* @author: zlm
* @date: 2018-6-25 10:40
* @description: 桶排序思路,每个次数对应一个桶
*/
class Solution347 {
public:
vector<int> topKFrequent(vector<int>& nums, int k) {
map<int, int> m;
vector<vector<int>> bucket(nums.size() + 1);
vector<int> res;
for (auto a : nums)
++m[a];
for (auto it : m) {
bucket[it.second].push_back(it.first);
}
for (int i = nums.size(); i >= 0; --i) {
for (int j = 0; j < bucket[i].size(); ++j) {
res.push_back(bucket[i][j]);
if (res.size() == k)
return res;
}
}
return res;
}
};
/** 题目 169. Majority Element
* @author: zlm
* @date: 2018-6-25 11:40
* @description: 数组中出现次数超过一半的元素
* @method: 对数组进行排序,超过一半的元素一定会出现在序列中间,即为数学上的中位数
* 所以问题转换为求解第 N/2 大的元素。
* 使用快速排序的 partition 思想,每次划分后,若基准元素的下标恰好为n/2,
* 则该基准元素即为第n/2大元素。
* 使用排序法在牛客上可以AC,但在leetcode中只能通过43/44个cases
*/
class Solution12 {
public:
int majorityElement(vector<int>& nums) {
if (nums.empty())
return -1;
int N = nums.size();
int low = 0, high = N - 1;
while (low <= high) {
int mid = partition(nums, low, high);
if (mid == N / 2) // mid 处于正中间位置,则一定是出现次数超过一半的那个元素
return nums[mid];
else if (mid < N / 2) {
low = mid + 1;
}
else
high = mid - 1;
}
// 这里还需要检查mid代表的元素出现次数是否超过一半
return -1;
}
int partition(vector<int> &num, int low, int high) {
// int low = 0, high = num.size() - 1;
int pivot = num[low];
while (low < high) {
while (low < high && pivot <= num[high])
high--;
num[low] = num[high];
while (low < high && pivot > num[low])
low++;
num[high] = num[low];
}
num[low] = pivot;
return low;
}
};
// 另一种解法:
// 1. 设置一个计数器,选择第一个元素为当前元素;
// 2. 如果下一个元素与当前元素相同,cnt++,否则cnt--;
// 3. 若cnt==0,则更改当前元素,并令cnt=1;
// 4. 最终cnt不为0,返回对应的当前元素
// 如果 majority 存在,则经过抵消和增加之后,cnt一定>=1;
class Solution13 {
public:
int majorityElement(vector<int>& nums) {
if (nums.empty())
return -1;
int cnt = 1;
int curElem = nums[0]; // 选择第一个元素用于和其他元素相抵消
for (int i = 1; i < nums.size(); i++) {
if (cnt != 0) {
if (curElem == nums[i])
cnt++;
else
cnt--;
}
else { // cnt == 0 时,需要重新选择当前元素并计数
curElem = nums[i];
cnt = 1;
}
}
// 还需要做一次验证,因为测试用例中可能不存在majority
return curElem; // 最终cnt>=1,此时经过抵消后,curElem即为出现次数过半的元素
}
};
/** 287. Find the Duplicate Number
* @author: zlm
* @date: 2018-6-25 11:40
* @description: 找出数组中非重复的元素(给定数组只有一个元素是非重复的,其他均为重复元素)
* 要求不能修改数组,只能读取数组元素,空间复杂度O(1)
* @method: 异或
*/
class Solution287 {
public:
int findDuplicate(vector<int>& nums) {
if (nums.size() == 0)
return 0;
int res = 0;
for (int i = 0; i < (int)nums.size(); i++) {
res ^= nums[i];
}
return res;
}
};
/** 5. Longest Palindromic Substring
* @author: zlm
* @date: 2018-6-25 22:12
* @description: 找出字符串中最长的回文子串
* @method: 动态规划法, 时间和空间复杂度均为 n^2
*/
class Solution15 {
public:
string longestPalindrome(string s) {
if (s.empty())
return string();
int len = s.length();
vector<vector<bool>> dp(len, vector<bool>(len));
int maxLength = 0; // 用于记录最大子串的长度
int startIndex = 0; // 记录最大子串的起始位置
for (int i = 0; i < len; i++) {
for (int j = i + 1; j < len; j++) {
if (j - i <= 1)
dp[i][j] = (s[i] == s[j]);
else {
dp[i][j] = (s[i] == s[j]) && dp[i + 1][j - 1];
}
// 不断记录子串的长度
if (dp[i][j] && maxLength < j - i + 1) {
maxLength = j - i + 1;
startIndex = i;
}
}
}
// 返回最大子串
return s.substr(startIndex, maxLength);
}
};
/** 53. Maximum Subarray
* @author: zlm
* @date: 2018-6-25 22:12
* @description: 求出一个数组中和最大的子数组(连续的),返回subArray的和
* @method: 这类题目比价常见, 直接遍历,O(n)
* 1. 从第一个元素开始,记录当前累加和curSum, 如果curSum>0,则继续+下一个元素
* 2. 若curSum < 0, 则即使加上下一个元素a[i],结果肯定小于a[i],故此时更新curSum = a[i],继续向后遍历
*/
class Solution53 {
public:
int maxSubArray(vector<int>& nums) {
if (nums.empty())
return 0;
int len = nums.size();
int curSum = 0; // 当前累加的和
int maxSum = INT_MIN;
for (int i = 0; i < len; i++) {
if (curSum < 0)
curSum = nums[i];
else {
curSum += nums[i]; // 记录当前累加和
}
if (curSum > maxSum)
maxSum = curSum;
}
return maxSum;
}
};
/** 70. Climbing Stairs: 台阶问题
* @author: zlm
* @date: 2018-6-25 22:30
* @description: 动态规划爬楼梯问题,给定n个台阶,每次限定只能走一阶或者两阶,求走台阶的方式个数
* @method: 主要是递推公式的推导
*/
class Solution70 {
public:
int climbStairs(int n) {
if (n <= 1)
return n;
// 设f(i)表示爬i阶楼梯的方案个数,i:1~n
// f(i) = f(i-1)+f(i-2) 最后一步可以走一阶也可以走两阶
vector<int> f(n + 1);
f[1] = 1;
f[2] = 2;
for (int i = 3; i <= n; i++) {
f[i] = f[i - 1] + f[i - 2];
}
return f[n];
}
int climbStairs2(int n) {
if (n <= 1)
return n;
int f0 = 1, f1 = 0, f = 0;
for (int i = 2; i <= n; i++) {
f = f0 + f1;
f0 = f1;
f1 = f;
}
return f;
}
};
/** 128. Longest Consecutive Sequence
* @author: zlm
* @date: 2018-6-25 22:30
* @description: 数组中最长的连续子序列,返回子序列的元素个数
* @method: hash表实现。中心扩展法:以当前元素a[i]为中心,分别寻找右侧和左侧的相邻元素
*/
class Solution128 {
public:
int longestConsecutive(vector<int>& nums) {
if (nums.empty())
return 0;
unordered_map<int, bool> used; // 用来表示数组中的元素是否被用过
for (auto v : nums)
used[v] = false;
int maxLength = 0;
for (auto i : nums) {
if (used[i])
continue; // 如果元素已经使用过,则没必要再次使用
int len = 1; // 记录当前查找的序列的长度
used[i] = true;
// 向右查找与元素i相邻的元素
for (int j = i + 1; used.find(j) != used.end(); j++) {
used[j] = true;
len++;
}
// 向左查找与元素i相邻的元素
for (int j = i - 1; used.find(j) != used.end(); j--) {
used[j] = true;
len++;
}
if (len > maxLength)
maxLength = len;
}
return maxLength;
}
};
/** 234. Palindrome Linked List
* @author: zlm
* @date: 2018-6-26 11:13
* @description: 判断单链表是否是回文链表
* @method: 要求空间复杂度为O(1),时间复杂度为O(n)
*/
class Solution234 {
public:
bool isPalindrome(ListNode* head) {
if (head == nullptr || head->next == nullptr)
return true;
int len = 0;
ListNode *tmp = head;
while (tmp != nullptr) {
len++;
tmp = tmp->next;
}
// 找到链表中心
tmp = head;
for (int i = 1; i <= (len - 1) / 2; i++) {
tmp = tmp->next;
}
// fast指向下标为(len-1)/2的结点
ListNode *head2 = tmp->next; // head2 指向链表的后半段
tmp->next = nullptr; // 前半段链表末尾置空
// head2指向的链表翻转
ListNode *revHead = Reverse(head2);
// 前后两端链表对应元素比较
// 如果是偶数,前后两段链表的长度均为n/2,如果是奇数,则不考虑中间元素
// 前后比较的元素个数仍然为n/2个
for (int i = 0; i < len / 2; i++) {
if (head->val != revHead->val)
return false;
head = head->next;
revHead = revHead->next;
}
return true;
}
// 翻转链表
ListNode* Reverse(ListNode *head) {
if (head == nullptr || head->next == nullptr)
return head; // 只有一个结点,返回head自身
ListNode *pcur = head->next;
ListNode *pre = head;
ListNode *pnext = nullptr;
while (pcur != nullptr) {
pnext = pcur->next;
pcur->next = pre;
pre = pcur;
pcur = pnext;
}
// 此时pre指向翻转后的链表首结点
head->next = nullptr;
return pre;
}
};
/** 647. Palindromic Substrings
* @author: zlm
* @date: 2018-6-26 15:02
* @description: 计算字符串中含有多少个回文子串
* @method: 比较高效的方式从每个字符开始,尝试扩展子字符串
*/
class Solution647 {
private:
int cnt = 0;
public:
int countSubstrings(string s) {
if (s.empty())
return 0;
for (int i = 0; i < s.length(); i++) {
HuiwenSubstring(s, i, i); // 奇数长度扩展
HuiwenSubstring(s, i, i + 1); // 偶数长度扩展,如‘abba’
}
return cnt;
}
// 判断回文的函数
void HuiwenSubstring(string &s, int left, int right) {
while (left >= 0 && right < s.length() && s[left] == s[right]) {
cnt++;
left--; // 向左右扩展字符串
right++;
}
}
};
/** 19. Remove Nth Node From End of List
* @author: zlm
* @date: 2018-6-26 15:02
* @description: 从链表末尾删除第 N 个结点,并返回链表head
* @method: 双指针:1. 从head开始,p1指针移动到第k个结点,此时剩余的结点个数为n-k
* 2. 指针 p2 从 head 开始,p2 和 p1 同时向后移动,每次均移动一步;
* 3. 当 p1 指向尾部结点时,p2 已经指向第 n-k+1 个结点, 即从后向前数第 k 个结点
*/
class Solution19 {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
if (head == nullptr)
return nullptr;
ListNode *p1 = head;
for (int i = 1; i <= n - 1 && p1 != nullptr; i++) { // p1 起始为第一个结点,向后移动 n-1 步
p1 = p1->next;
}
if (p1 == nullptr) // 说明结点的个数小于 n
return nullptr;
// 否则说明p1此时指向正向第 n 个结点
ListNode *p2 = head;
ListNode *pre = nullptr; // 用于记录 p2 结点的前一个结点
while (p1->next != nullptr) { // p1向后移动 len-n 个结点
p1 = p1->next;
pre = p2;
p2 = p2->next;
}
// 此时 p2 即为反向第 n 个结点, pre指向 p2 的前一个结点,反向第n+1个结点
if (p2 == head) { // 如果待删除的结点为首结点head
head = p2->next;
delete p2;
p2 = nullptr;
}
else {
pre->next = p2->next;
delete p2;
p2 = nullptr;
}
return head;
}
};
/** 136. Single Number, easy
* @author: zlm
* @date: 2018-6-26 15:02
* @description: 找出只出现一次的数字
* @method: 数组中只有一个数只出现一次,其余均出现两次,故可以使用异或来处理!
*/
class Solution136 {
public:
int singleNumber(vector<int>& nums) {
if (nums.empty())
return 0;
int res = 0; // 0^anyNum = anyNum
for (int n : nums) {
res = res ^ n;
}
return res;
}
};
// 排列组合类题目
/** 46. Permutations
* @author: zlm
* @date: 2018-6-26 15:02
* @description: 全排列问题
* @method: 递归,先确定一个数,剩下的 n-1 个数数递归进行排列
*/
class Solution46 {
public:
vector<vector<int>> permute(vector<int>& nums) {
vector<vector<int>> res; // 存放结果
if (nums.empty())
return res;
permutation(nums, 0, res);
return res;
}
/* 核心函数
* 参数:nums,数组;res:存放所有的排列;begin:当前排列时第一个元素的下标
*/
void permutation(vector<int> &nums, int begin, vector<vector<int>> &res) {
if (begin == nums.size()-1) { //如果遍历到最后一个元素,则把此次排列存入 res 中
res.push_back(nums); // 因为这里是使用nums本身进行排列,故不需要return;
}
else {
// 依次把后面元素跟第一个位置的元素交换
for (int i = begin; i < nums.size(); i++) {
swap(nums[begin], nums[i]);
//递归的对 begin 后面的元素进行排列
permutation(nums, begin + 1, res);
// 排列后需要把原来的交换的元素再交换回来
swap(nums[begin], nums[i]);
// 开始下次循环,即使用下一个数作为首元素,再次进行排列
}
}
}
};
/** 77. Combinations
* @author: zlm
* @date: 2018-6-26 20:50
* @description: 组合问题:在 1,2,3,..,n-1, n 中选出由 k 个数构成的所有组合
* @method: backtracing, DFS, 递归
* 先找出1开头的组合->2开头的组合->3开头的组合...需要对n个数进行DFS,故时间复杂度为 O(n!)
*/
class Solution77 {
public:
vector<vector<int>> combine(int n, int k) {
vector<vector<int>> res;
if (n <= 0 || k > n)
return res;
vector<int> cur; // 当前组合
combination(res, cur, n, k, 1); // 组合中的数字范围为:1~n
return res;
}
// 组合的核心函数
// begin:表示组合的起始元素。即求出从 begin 开始的元素
void combination(vector<vector<int>> &res, vector<int> &cur, int n, int k, int begin) {
if (cur.size() == k) { // 回溯的终止条件:找到一种组合后函数返回
res.push_back(cur);
return;
}
// 先确定首元素 i, 求出以 i 开头的组合
for (int i = begin; i <= n; i++) {
cur.push_back(i);
// 递归
combination(res, cur, n, k, i + 1); //确定了首元素 i, 然后对i之后的元素进行组合
// 回溯后,删除当前for循环中添加的元素(line717),此时cur = [],下一次 fo r循环中将选择新的首元素
cur.pop_back();
}
}
};
/** 617. Merge Two Binary Trees
* @author: zlm
* @date: 2018-6-27 Wed
* @description: 把两个二叉树对应结点元素相加,然后返回合并后的二叉树
* @method:递归实现:先合并root,然后对左子树和右子树递归进行合并
*/
class Solution617 {
public:
TreeNode* mergeTrees(TreeNode* t1, TreeNode* t2) {
if (t1 == nullptr && t2 != nullptr)
return t2;
else if (t2 == nullptr && t1 != nullptr)
return t1;
else if (t1 == nullptr && t2 == nullptr)
return nullptr;
TreeNode *root = new TreeNode(0);
root->val = t1->val + t2->val; // 合并根结点
// 递归对左子树和右子树进行合并
root->left = mergeTrees(t1->left, t2->left);
root->right = mergeTrees(t1->right, t2->right);
return root;
}
};
/** 113. Path Sum II
* @author: zlm
* @date: 2018-6-27 16:13
* @description: 在二叉树中,找到所有结点和 =sum 的路径(这里的路径均为从 root结点->leaf结点的路径)
* @method: 回溯,深度优先搜索 DFS
*/
class Solution113 {
public:
vector<vector<int>> pathSum(TreeNode* root, int sum) {
vector<vector<int>> res;
if (root == nullptr)
return res;
vector<int> cur;
FindPath(root, res, cur, sum);
return res;
}
// 递归函数
void FindPath(TreeNode *root, vector<vector<int>> &res, vector<int> &cur, int sum) {
if (root == nullptr)
return;
cur.push_back(root->val); // 先存入根结点
if (root->left == nullptr && root->right == nullptr) { // 到达叶子结点后,当前路径已近确定,判断其路径和
if (accumulate(cur.begin(), cur.end(), 0) == sum)
res.push_back(cur);
} else {
if (root->left != nullptr)
FindPath(root->left, res, cur, sum);
if (root->right != nullptr)
FindPath(root->right, res, cur, sum);
}
// 回溯后,删除当前存入的结点
cur.pop_back();
}
};
//----------- Dynamic Programming examples -----------
/** 120. Triangle
* @author: zlm
* @date: 2018-7-11 16:43
* @description: 三角形中,从顶点到最底层的最短路径,每次只能移动到下一层的相邻结点
* @method: 动态规划__自上而下:
* f[i,j] 表示从(0,0)点到点(i,j)的最短路径和
* 到达(i,j)的前一个点只能是:(i-1,j-1),(i-1,j)
* 故f[i,j] = min(f[i-1,j], f[i-1,j-1]) + tri[i,j];
*/
class Solution120 {
public:
int minimumTotal(vector<vector<int>>& triangle) {
if (triangle.empty())
return 0;
int row = triangle.size();
vector<vector<int>> dp(row, vector<int>(row)); // 默认初值为0
// 动态表赋初值
dp[0][0] = triangle[0][0]; // 第一行只有一个数
for (int i = 1; i < row; i++) {
for (int j = 0; j < row; j++) {
if (j == 0) // 对于第一列,(i,j)的前一个落点只能是(i-1,j)
dp[i][j] = dp[i - 1][j] + triangle[i][j];
else if (j == triangle[i].size() - 1) // 当前行的最后一列
dp[i][j] = dp[i - 1][j - 1] + triangle[i][j];
else
dp[i][j] = min(dp[i - 1][j - 1], dp[i - 1][j]) + triangle[i][j];
}
}
// dp[row-1][j]存放着从(0,0)到最后一层各点的最短路径
int minimum = dp[row - 1][0];
for (int i = 1; i < row; i++) {
if (dp[row - 1][i] < minimum)
minimum = dp[row - 1][i];
}
return minimum;
}
};
/** 64. Minimum Path Sum
* @author: zlm
* @date: 2018-7-11 17:43
* @description: 从矩阵的左上角到右下角的最小路径和,每次只能向左和向下移动。
* @method: 动态规划
*/
class Solution64 {
public:
int minPathSum(vector<vector<int>>& grid) {
if (grid.empty() || grid[0].empty())
return 0;
int row = grid.size();
int col = grid[0].size();
vector<vector<int>> dp(row, vector<int>(col));
// dp[0][0] = grid[0][0];
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
if (i == 0 && j == 0)
dp[i][j] = grid[i][j];
else if (j == 0 && i >= 1) // 第一列
dp[i][j] = dp[i - 1][j] + grid[i][j];
else if (i == 0 && j >= 1) // 计算第一行
dp[i][j] = dp[i][j - 1] + grid[i][j];
else {
dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j];
}
}
}
return dp[row - 1][col - 1];
}
};
/** 63. Unique Paths II: 带有障碍物,求路径个数
* @author: zlm
* @date: 2018-7-11 20:48
* @description: 从矩阵的左上角到右下角的最小路径和,每次只能向左和向下移动。
* @method: 动态规划
*/
class Solution63 {
public:
int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
// 边界条件
if (obstacleGrid.empty() || obstacleGrid[0].empty())
return 0;
int row = obstacleGrid.size();
int col = obstacleGrid[0].size();
vector<vector<int>> dp(row, vector<int>(col));
// 确定初值
dp[0][0] = obstacleGrid[0][0] == 0 ? 1 : 0; // 有障碍时,路径个数为0
// 第一列上的点只能向下走,注意障碍物
for (int i = 1; i < row; i++) {
dp[i][0] = (dp[i - 1][0] == 1 && obstacleGrid[i][0] == 0) ? 1 : 0; // 如果上一个点有路径,且当前点无障碍,则dp[i][0] = 1
}
// 第一行上的点只能向右走,注意障碍物
for (int j = 1; j < col; j++) {
dp[0][j] = (dp[0][j - 1] == 1 && obstacleGrid[0][j] == 0) ? 1 : 0; // 如果上一个点有路径,且当前点无障碍,则dp[0][j] = 1
}
// 其它点的递推公式
for (int i = 1; i < row; i++) {
for (int j = 1; j < col; j++) {
if (obstacleGrid[i][j] == 1)
dp[i][j] = 0;
else
dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
}
}
return dp[row - 1][col - 1];
}
};
/** 69. Sqrt(x): 求平方根
* @author: zlm
* @date: 2018-7-11 21:59
* @description: xxx
* @method: 二分查找法、牛顿迭代法
*/
class Solution69 {
public:
int mySqrt(int x) {
if (x <= 1)
return x;
int low = 1, high = x; // 对于大于1的正数,显然sqrt(x) < x
int mid;
while (low <= high) {
mid = (low + high) / 2;
if (mid * mid == x)
return mid;
else if (mid * mid < x)
low = mid + 1;
else high = mid - 1;
}
return high;
}
};
// 牛顿迭代法: Xi+1 = Xi - F(Xi)/F'(Xi);
// 求x = sqrt(n), 相当于求x^2-n = 0, 令fx = x^2 - n ,可推导出 迭代公式 Xi+1 = (Xi + n/Xi)/2;
int sqrt2(int x) {
if (x <= 1)
return x;
int s0 = 0, s1 = 1; //迭代初始值
while (abs(s1 - s0) > 1.0e-6) {
// 不满足精度,接着迭代求解
s0 = s1;
s1 = (s0 + x / s0) / 2;
}
return s0;
}
/** 135.Candy(贪心法实例)
* date: 2018-7-13 19:14
* author:zlm
* 思路:贪心思想。
* 首先每个小孩分一个糖果。设下标为i的孩子的糖果数量为num[i],等级为r[i]。
* 1. 从左到右扫描,第i个孩子等级 < 第i+1个孩子,num[i+1]=num[i]+1;
* 2. 再从右向左扫描,第 i 个孩子等级 > 第i+1个孩子,同时第i个孩子的糖果数量 < 第i+1个孩子,则num[i]=num[i+1]+1;
* 时间复杂度: 16ms,beats 98.51%
*/
class Solution135 {
public:
int candy(vector<int>& ratings) {
if (ratings.empty())
return 0;
int N = ratings.size();
vector<int> candy(N, 1); // 每个人至少一块糖果
// 从左向右扫描,找右侧等级高的孩子
for (int i = 0; i < N-1; i++) {
if (ratings[i] < ratings[i + 1])
candy[i + 1] = candy[i] + 1;
}
// 从右向左扫描,找左侧等级高的孩子
for (int i = N - 1; i >= 1; i--) {
if (ratings[i - 1] > ratings[i] && candy[i - 1] <= candy[i])
candy[i - 1] = candy[i] + 1;
}
// 返回candy之和
int sum = 0;
for (int i : candy)
sum += i;
return sum;
}
};
/** 55. Jump Game 跳跃游戏, 8ms
* @author: zlm
* @date: 2018-7-13 20:43
* @description: 给一个数组,里面每个元素表示可以向后跳跃的步数,我们需要知道能不能移动到最后一个元素位置。
* @method: 贪心法,
* 1. 假设当前可跳跃步数为 v,每次先跳跃一步,则剩余步数为 v-1,若此时新的位置的步数 s 大于 v-1,则使用新的步数继续移动;
* 2. 由于剩余可跳跃次数 v-1 小于0,且没有到达数组末尾,说明当前已经无法再移动,即跳跃失败
* 上述过程中,每跳跃到一个新的位置,都会选择最大的可用步数继续向前移动,是贪心思想的体现!
*/
class Solution55 {
public:
bool canJump(vector<int>& nums) {
// 向量为空,返回false
if (nums.empty())
return false;
int len = nums.size();
int step = nums[0]; // 从第一个元素开始跳跃
for (int j = 1; j < len; j++) {
step--; // 每次都先跳跃一步,然后比较剩余步数 (step-1) 与新位置的步数 nums[j]