时间: 2021-08-23 21:14:55—->2021-08-26 10:43:12
完结撒花
1 | 反转链表 https://leetcode-cn.com/problems/reverse-linked-list/
1 2 3 4 5 6 7 8 9 10 11 class Solution {public : ListNode* reverseList (ListNode* head) { if (!head) return head; ListNode* pre = nullptr , *nex = nullptr ; while (head != nullptr ) { nex = head->next, head->next = pre, pre = head, head = nex; } return pre; } };
2 | 无重复字符的最长子串 https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Solution {public : int lengthOfLongestSubstring (string s) { unordered_set <char > st; int ans = 0 ; for (int r = 0 , l = 0 ; r < s.size(); ++r) { if (st.count(s[r])) { while (s[r] != s[l]) st.erase(s[l++]); st.erase(s[l++]); } st.insert(s[r]); ans = max(ans, r - l + 1 ); } return ans; } };
3 | LRU 缓存机制 note:
自己写链表还是太麻烦了
一定要记得将「移动到头部」方法提取出来, 因为在get和put中都需要用, 一共用三次, 而链表的移动一定要考虑清楚, 步骤不要写丢了
https://leetcode-cn.com/problems/lru-cache/
自己实现双向链表
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 struct double_list { int key, value; double_list* next, *prev; double_list(): key(0 ), value(0 ), prev(nullptr ), next(nullptr ) {} };class LRUCache {public : unordered_map <int , double_list*> hmap; double_list* head, *tail; int sz; int thresh; LRUCache(int capacity): thresh(capacity), sz(0 ) { head = new double_list(); tail = new double_list(); head->next = tail, tail->prev = head; } void add_head (double_list* now) { if (now->next && now->prev) { now->next->prev = now->prev; now->prev->next = now->next; } now->next = head->next; head->next->prev = now; now->prev = head; head->next = now; } int get (int key) { if (!hmap.count(key)) { return -1 ; } else { double_list* now = hmap[key]; add_head(now); return now->value; } } void put (int key, int value) { if (!hmap.count(key)) { ++sz; double_list* now = new double_list(); now->value = value; now->key = key; add_head(now); hmap[key] = now; if (sz > thresh) { --sz; double_list* pre = tail->prev; tail->prev = tail->prev->prev; pre->prev->next = tail; hmap.erase(pre->key); delete pre; } } else { double_list* now = hmap[key]; now->value = value; add_head(now); } } };
使用list
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 class LRUCache {public : int sz; unordered_map <int , list <pair <int , int >>::iterator> hmap; list <pair <int , int >> cache; LRUCache(int capacity): sz(capacity) {} int get (int key) { if (hmap.count(key)) { cache.splice(cache.begin(), cache, hmap[key]); return hmap[key]->second; } else { return -1 ; } } void put (int key, int value) { if (!hmap.count(key)) { cache.emplace_front(key, value); hmap[key] = cache.begin(); if (cache.size() > sz) { hmap.erase(cache.back().first); cache.pop_back(); } } else { hmap[key]->second = value; cache.splice(cache.begin(), cache, hmap[key]); } } };
4 | 数组中的第K个最大元素 https://leetcode-cn.com/problems/kth-largest-element-in-an-array/
note:
此题考查的点:
快速排序 (复杂度为O(N)) => 如何证明?
堆排序 (复杂度O(NlogN))
堆排序写法:
堆排序 + 删除k次最顶端元素:
堆排的方法:
1. 从 N / 2开始每次sink建立大顶堆
2. 删除最顶端元素方法: 将最顶端和最后一个交换后sink一次
总复杂度: O(nlogn)
快速排序写法
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 class Solution {public : int findKthLargest (vector <int >& nums, int k) { srand(time(0 )); random_shuffle(nums.begin(), nums.end()); int n = nums.size(); return select(nums, 0 , n, n - k); } int select (vector <int >& nums, int l, int r, int k) { int idx = partition(nums, l, r); if (idx == k) return nums[idx]; return idx < k ? select(nums, idx + 1 , r, k) : select(nums, l, idx, k); } int partition (vector <int >& nums, int l, int r) { int i = l, j = r; int x = nums[l]; while (i < j) { while (j > i && nums[--j] > x); while (j > i && nums[++i] < x); swap(nums[i], nums[j]); } swap(nums[l], nums[i]); return i; } };
自建大顶堆, k次删除
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 class Solution {public : void sink (vector <int >& x, int k, int n) { while ((k << 1 ) <= n) { int j = k << 1 ; if (j < n && x[j] < x[j + 1 ]) ++j; if (x[k] >= x[j]) break ; swap(x[k], x[j]); k = j; } } void build (vector <int >& x, int n) { for (int k = n >> 1 ; k >= 1 ; --k) { sink(x, k, n); } } int select (vector <int >& x, int k, int n) { k--; while (k--) { swap(x[n], x[1 ]); x.pop_back(); sink(x, 1 , --n); } return x[1 ]; } int findKthLargest (vector <int >& nums, int k) { nums.insert(nums.begin(), 0 ); int n = nums.size() - 1 ; build(nums, n); return select(nums, k, n); } };
5 | K 个一组翻转链表
寻觅的最简单的一种写法
变量定义个数: (prev, nex, tail, now)共四个
其中prev和now用于遍历和调换顺序
tail和head用于控制头尾
最后head指向「尾部」! prev指向「头部」! now和tail指向「下一组的头部」
https://leetcode-cn.com/problems/reverse-nodes-in-k-group/
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 class Solution {public : ListNode* reverseKGroup (ListNode* head, int k) { if (!head) return head; ListNode* tail = head; for (int i = 0 ; i < k; ++i) { if (!tail) return head; tail = tail->next; } ListNode* now = head; ListNode* prev = tail, *nex = nullptr ; while (now != tail) { nex = now->next, now->next = prev, prev = now, now = nex; } head->next = reverseKGroup(tail, k); return prev; } };
6 | 排序数组 https://leetcode-cn.com/problems/sort-an-array/
快速排序
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 class Solution {public : vector <int > sortArray (vector <int >& nums) { int l = 0 , r = nums.size(); random_shuffle(nums.begin(), nums.end()); quick_sort(nums, l, r); return nums; } void quick_sort (vector <int >& nums, int l, int r) { if (l >= r) return ; int idx = partition(nums, l, r); quick_sort(nums, l, idx); quick_sort(nums, idx + 1 , r); } int partition (vector <int >& nums, int l, int r) { int i = l, j = r; int x = nums[l]; while (i < j) { while (j > i && nums[--j] > x); while (j > i && nums[++i] < x); swap(nums[i], nums[j]); } swap(nums[i], nums[l]); return i; } };
归并排序
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 class Solution {public : vector <int > aux; vector <int > sortArray (vector <int >& nums) { int n = nums.size(); aux = nums; int l = 0 , r = nums.size(); merge_sort(nums, l, r - 1 ); return nums; } void merge_sort (vector <int >& nums, int l, int r) { if (l >= r) return ; int mid = l + r >> 1 ; merge_sort(nums, l, mid); merge_sort(nums, mid + 1 , r); merge(nums, l, mid, r); } void merge (vector <int >&nums, int l, int mid, int r) { int p1 = l, p2 = mid + 1 , k = l; while (p1 <= mid && p2 <= r) { if (nums[p1] < nums[p2]) { aux[k++] = nums[p1++]; } else { aux[k++] = nums[p2++]; } } while (p1 <= mid) aux[k++] = nums[p1++]; while (p2 <= r) aux[k++] = nums[p2++]; for (int i = l; i <= r; ++i) { nums[i] = aux[i]; } } };
堆排序
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 class Solution {public : void sink (vector <int >& nums, int k, int n) { while (k * 2 + 1 < n) { int left = k * 2 + 1 ; int right = k * 2 + 2 ; int choice = left; if (left < n - 1 && nums[right] > nums[left]) choice = right; if (nums[choice] <= nums[k]) break ; swap(nums[choice], nums[k]); k = choice; } } void build (vector <int >& nums, int n) { for (int i = n / 2 ; i >= 0 ; --i) { sink(nums, i, n); } } vector <int > sortArray (vector <int >& nums) { int n = nums.size(); build(nums, n); int k = n - 1 ; for (int i = 0 ; i < n - 1 ; ++i) { swap(nums[0 ], nums[k]); sink(nums, 0 , k--); } return nums; } };
7 | 两数之和 https://leetcode-cn.com/problems/two-sum/submissions/
1 2 3 4 5 6 7 8 9 10 11 12 class Solution {public : vector <int > twoSum (vector <int >& nums, int target) { unordered_map <int , int > hmap; int k = 0 ; for (auto i : nums) { if (hmap.count(target - i)) return {k, hmap[target - i]}; else hmap[i] = k++; } return {}; } };
8 | 三数之和 https://leetcode-cn.com/problems/3sum/submissions/
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 class Solution {public : vector <vector <int >> threeSum(vector <int >& nums) { vector <vector <int >> ans; sort(nums.begin(), nums.end()); int n = nums.size(); for (int l = 0 ; l < n - 2 ; ++l) { if (l != 0 && nums[l] == nums[l - 1 ]) continue ; int r = n - 1 , k = l + 1 ; int target = -nums[l]; while (k < r) { int cur = nums[k] + nums[r]; if (cur > target) { --r; } else if (cur < target) { ++k; } else if (cur == target) { if (r == n - 1 || nums[r] != nums[r + 1 ]) ans.push_back({nums[l], nums[k], nums[r]}); ++k, --r; } } } return ans; } };
9 | 最大子序和 https://leetcode-cn.com/problems/maximum-subarray/
1 2 3 4 5 6 7 8 9 10 11 12 13 class Solution {public : int maxSubArray (vector <int >& nums) { int ans = INT_MIN, t = 0 ; for (auto i : nums) { t = max(t + i, i); ans = max(ans, t); } return ans; } };
10 | 合并两个有序链表 https://leetcode-cn.com/problems/merge-two-sorted-lists/
减少特判是增加可读性的必要条件
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 class Solution {public : ListNode* mergeTwoLists (ListNode* l1, ListNode* l2) { ListNode* dummy = new ListNode(0 , l1); ListNode* now = dummy; while (l1 && l2) { if (l1->val < l2->val) { now->next = l1; l1 = l1->next; } else { now->next = l2; l2 = l2->next; } now = now->next; } if (l1) now->next = l1; else now->next = l2; return dummy->next; } };
11 | 环形链表 只需要判断fast是否到达null即可, 因为fast走的快
https://leetcode-cn.com/problems/linked-list-cycle/submissions/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Solution {public : bool hasCycle (ListNode *head) { if (!head) return false ; ListNode* slow = head, *fast = head; while (fast && fast->next) { fast = fast->next->next; slow = slow->next; if (fast == slow) return true ; } return false ; } };
12 | 相交链表 因为最后不管是否相交总会走到空, 故无需特判
https://leetcode-cn.com/problems/intersection-of-two-linked-lists/submissions/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Solution {public : ListNode *getIntersectionNode (ListNode *headA, ListNode *headB) { ListNode* p1 = headA, *p2 = headB; while (p1 != p2) { p1 = p1 == nullptr ? headB : p1->next; p2 = p2 == nullptr ? headA : p2->next; } return p1; } };
13 | 二叉树的锯齿形层序遍历 https://leetcode-cn.com/problems/binary-tree-zigzag-level-order-traversal/
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 class Solution {public : vector <vector <int >> zigzagLevelOrder(TreeNode* root) { if (!root) return {}; int flag = true ; vector <vector <int >> ans; queue <TreeNode*> q; q.push(root); while (q.size()) { flag = !flag; vector <int > now; int sz = q.size(); while (sz--) { auto k = q.front(); q.pop(); now.push_back(k->val); if (k->left) q.push(k->left); if (k->right) q.push(k->right); } if (flag) reverse(now.begin(), now.end()); ans.push_back(now); } return ans; } };
14 | 二叉树的层序遍历 上一题的简化版
https://leetcode-cn.com/problems/binary-tree-level-order-traversal/ )
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 class Solution {public : vector <vector <int >> levelOrder(TreeNode* root) { if (!root) return {}; vector <vector <int >> ans; queue <TreeNode*> q; q.push(root); while (q.size()) { vector <int > now; int sz = q.size(); while (sz--) { auto k = q.front(); q.pop(); now.push_back(k->val); if (k->left) q.push(k->left); if (k->right) q.push(k->right); } ans.push_back(now); } return ans; } };
15 | 买卖股票的最佳时机 https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/
1 2 3 4 5 6 7 8 9 10 11 12 class Solution {public : int maxProfit (vector <int >& prices) { int minn = INT_MAX >> 1 ; int ans = 0 ; for (auto i : prices) { ans = max(ans, i - minn); minn = min(minn, i); } return ans; } };
16 | 有效的括号 https://leetcode-cn.com/problems/valid-parentheses/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Solution {public : bool isValid (string s) { unordered_map <char , char > hmap = {{']' , '[' }, {')' , '(' }, {'}' , '{' }}; stack <char > stk; for (auto i : s) { if (hmap.count(i)) { if (stk.empty()) return false ; auto now = stk.top(); stk.pop(); if (now != hmap[i]) return false ; } else { stk.push(i); } } return stk.empty(); } };
17 | 合并两个有序数组 https://leetcode-cn.com/problems/merge-sorted-array/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Solution {public : void merge (vector <int >& nums1, int m, vector <int >& nums2, int n) { int k = m + n - 1 ; --m, --n; while (m >= 0 && n >= 0 ) { nums1[k--] = nums2[n] > nums1[m] ? nums2[n--] : nums1[m--]; } while (n >= 0 ) { nums1[k--] = nums2[n--]; } } };
18 | 字符串相加 https://leetcode-cn.com/problems/add-strings/
模拟竖式加法后逆序
减少特判! 和T37「两数相加」的实现方法基本是一致的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Solution {public : string addStrings (string nums1, string nums2) { int carry = 0 ; string res; int p1 = nums1.size() - 1 , p2 = nums2.size() - 1 ; while (p1 >= 0 || p2 >= 0 || carry) { int a = 0 , b = 0 ; if (p1 >= 0 ) a = nums1[p1--] - '0' ; if (p2 >= 0 ) b = nums2[p2--] - '0' ; res += (a + b + carry) % 10 + '0' ; carry = (a + b + carry) / 10 ; } reverse(res.begin(), res.end()); return res; } };
19 | 二叉树的最近公共祖先 https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-tree/
自底向上
不用担心重复更新res的问题, 因为res更新的情况仅仅只会出现一次
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 class Solution {public : TreeNode* res; bool check (TreeNode* root, TreeNode* p, TreeNode* q) { if (!root) return false ; bool lflag = check(root->left, p, q); bool rflag = check(root->right, p, q); if (lflag && rflag || (lflag || rflag) && (root == p || root == q)) { res = root; return true ; } return lflag || rflag || root == p || root == q; } TreeNode* lowestCommonAncestor (TreeNode* root, TreeNode* p, TreeNode* q) { check(root, p, q); return res; } };
20 | 环形链表2 https://leetcode-cn.com/problems/linked-list-cycle-ii/
需要利用的条件:
快指针走的是满指针2倍速
快指针比慢指针多走n圈
然后写出公式, 考虑a和c的关系
对快慢指针进行分析:
当快慢指针相遇时:
快指针走了: a + b + n * circle
慢指针走了: a + b
令 c = circle - b ==> b + c = circle
又已知快指针是满指针的两倍: 即: a + b = n * circle
由 a + b = n * circle 和 b + c = circle
相减得到a - c = (n - 1) * circle
即a - c是circle的整数倍
那么当快慢指针相遇时候, 让一个指针从起点开始和慢指针一起走, 当他走到a时候, 慢指针走了 c + k圈, 最后会在起点相遇
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 class Solution {public : ListNode *detectCycle (ListNode *head) { ListNode* slow = head, *fast = head; while (fast && fast->next) { fast = fast->next->next; slow = slow->next; if (fast == slow) break ; } if (!fast || !fast->next) return nullptr ; ListNode* p = head; while (p != slow) { slow = slow->next; p = p->next; } return slow; } };
21 | 搜索旋转排序数组 https://leetcode-cn.com/problems/search-in-rotated-sorted-array/
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 class Solution {public : int search (vector <int >& nums, int target) { int n = nums.size(); int l = 0 , r = n - 1 ; while (l <= r) { int mid = l + r >> 1 ; if (nums[mid] == target) return mid; if (nums[mid] >= nums[0 ]) { if (target >= nums[0 ] && target < nums[mid]) { r = mid - 1 ; } else { l = mid + 1 ; } } else { if (target > nums[mid] && target <= nums[n - 1 ]) { l = mid + 1 ; } else { r = mid - 1 ; } } } return -1 ; } };
22 | 全排列 https://leetcode-cn.com/problems/permutations/
利用标记数组的写法 (可以保证字典序):
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 class Solution {public : vector <int > vis; int n; vector <vector <int >> ans; void dfs (int k, vector <int > now, vector <int >& nums) { if (k == n) { ans.push_back(now); return ; } for (int i = 0 ; i < n; ++i) { if (!vis[i]) { vis[i] = 1 ; now.push_back(nums[i]); dfs(k + 1 , now, nums); now.pop_back(); vis[i] = 0 ; } } } vector <vector <int >> permute(vector <int >& nums) { n = nums.size(); vis.resize(n, 0 ); vector <int > now; dfs(0 , now, nums); return ans; } };
利用交换的写法, 减少了空间复杂度:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Solution {public : vector <vector <int >> ans; vector <vector <int >> permute(vector <int >& nums) { int n = nums.size(); dfs(nums, 0 , n); return ans; } void dfs (vector <int >& nums, int now, int n) { if (now == n) { ans.push_back(nums); return ; } for (int i = now; i < n; ++i) { swap(nums[now], nums[i]); dfs(nums, now + 1 , n); swap(nums[now], nums[i]); } } };
23 | 二分查找 https://leetcode-cn.com/problems/binary-search/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Solution {public : int search (vector <int >& nums, int target) { int l = 0 , r = nums.size(); while (l < r) { int mid = l + r >> 1 ; if (nums[mid] == target) return mid; else if (nums[mid] < target) { l = mid + 1 ; } else { r = mid; } } return -1 ; } };
24 | 螺旋矩阵 https://leetcode-cn.com/problems/spiral-matrix/submissions/
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 class Solution {public : vector <int > spiralOrder (vector <vector <int >>& matrix) { if (matrix.empty()) return {}; int n = matrix.size(), m = matrix[0 ].size(); int left = 0 , right = m - 1 , top = 0 , bottom = n - 1 ; vector <int > ans; while (1 ) { for (int i = left; i <= right; ++i) { ans.push_back(matrix[top][i]); } if (++top > bottom) break ; for (int i = top; i <= bottom; ++i) { ans.push_back(matrix[i][right]); } if (--right < left) break ; for (int i = right; i >= left; --i) { ans.push_back(matrix[bottom][i]); } if (--bottom < top) break ; for (int i = bottom; i >= top; --i) { ans.push_back(matrix[i][left]); } if (++left > right) break ; } return ans; } };
25 | 最长回文子串 https://leetcode-cn.com/problems/longest-palindromic-substring/submissions/
方案1: 动态规划 (注意考虑长度为1和长度为2的作为初始化)
初始化: dp[i][i]
和dp[i][i + 1]
转移: dp[i][j] = dp[i + 1][j - 1] && s[i] == s[j]
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 class Solution {public : string longestPalindrome (string s) { int n = s.size(); vector <vector <int >> dp(n, vector <int > (n, 0 )); int ans = 1 , idx = 0 ; for (int i = 0 ; i < n; ++i) { dp[i][i] = 1 ; if (i != n - 1 && s[i] == s[i + 1 ]) { dp[i][i + 1 ] = 1 ; ans = 2 ; idx = i; } } for (int len = 2 ; len <= n; ++len) { for (int i = 0 ; i < n; ++i) { int j = i + len - 1 ; if (j >= n) break ; if (dp[i + 1 ][j - 1 ] && (s[i] == s[j])) { dp[i][j] = 1 ; if (j - i + 1 > ans) { ans = j - i + 1 ; idx = i; } } } } return s.substr(idx, ans); } };
方案2. 中心扩展(trick很多, 建议熟记!!)
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 class Solution {public : int check (string s, int l, int r) { while (l >= 0 && r <= s.size() - 1 && s[l] == s[r]) { --l, ++r; } return r - l - 1 ; } string longestPalindrome (string s) { int start = 0 , len = 1 ; for (int i = 0 ; i < s.size(); ++i) { int now_len = max(check(s, i, i), check(s, i, i + 1 )); if (now_len > len) { start = i - (now_len - 1 ) / 2 ; len = now_len; } } return s.substr(start, len); } };
26 | 岛屿数量
这里卡了一会, 注意bfs标记的时候要立即标记不要取出队列以后再标记
https://leetcode-cn.com/problems/number-of-islands/
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 class Solution {public : vector <int > dir = {-1 , 0 , 1 , 0 , -1 }; void bfs (int i, int j, vector <vector <char >>& grid) { queue <pair <int , int >> q; q.emplace(i, j); grid[i][j] = '3' ; while (q.size()) { auto [x, y] = q.front(); q.pop(); for (int k = 0 ; k < 4 ; ++k) { int nx = x + dir[k], ny = y + dir[k + 1 ]; if (nx < 0 || nx >= grid.size() || ny < 0 || ny >= grid[0 ].size() || grid[nx][ny] != '1' ) continue ; if (grid[nx][ny] == '1' ) { grid[nx][ny] = '3' ; q.emplace(nx, ny); } } } }; int numIslands (vector <vector <char >>& grid) { int ans = 0 ; for (int i = 0 ; i < grid.size(); ++i) { for (int j = 0 ; j < grid[0 ].size(); ++j) { if (grid[i][j] == '1' ) { ++ans; bfs(i, j, grid); } } } return ans; } };
27 | 接雨水 https://leetcode-cn.com/problems/trapping-rain-water/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Solution {public : int trap (vector <int >& height) { int n = height.size(); int leftmax = height[0 ], rightmax = height[n - 1 ]; int l = 0 , r = n - 1 ; int ans = 0 ; while (l <= r) { if (leftmax < rightmax) { ans += max(leftmax - height[l], 0 ); leftmax = max(leftmax, height[l++]); } else { ans += max(rightmax - height[r], 0 ); rightmax = max(rightmax, height[r--]); } } return ans; } };
28 | 最长上升子序列 https://leetcode-cn.com/problems/longest-increasing-subsequence/submissions/
动态规划解法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Solution {public : int lengthOfLIS (vector <int >& nums) { int n = nums.size(); vector <int > dp (n, 1 ) ; int ans = 1 ; for (int i = 1 ; i < n; ++i) { for (int j = i - 1 ; j >= 0 ; --j) { if (nums[i] > nums[j]) { dp[i] = max(dp[i], dp[j] + 1 ); ans = max(ans, dp[i]); } } } return ans; } };
贪心解法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Solution {public : int lengthOfLIS (vector <int >& nums) { vector <int > res; for (auto i : nums) { if (res.empty() || i > res.back()) res.push_back(i); else { auto it = lower_bound(res.begin(), res.end(), i); *it = i; } } return res.size(); } };
29 | 反转链表2
所有链表反转类的题目基本类似
https://leetcode-cn.com/problems/reverse-linked-list-ii/submissions/
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 class Solution {public : ListNode* reverseBetween (ListNode* head, int left, int right) { if (!head) return head; ListNode* dummy = new ListNode(0 , head); ListNode* slow = dummy, *fast = dummy; for (int i = 0 ; i < right - left + 1 ; ++i) { fast = fast->next; } for (int i = 1 ; i < left; ++i) { slow = slow->next; fast = fast->next; } ListNode* tail = fast->next, *before = slow; ListNode* prev = tail, *nex = nullptr ; slow = slow->next; while (slow != tail) { nex = slow->next, slow->next = prev, prev = slow, slow = nex; } before->next = prev; return dummy->next; } };
30 | 用栈实现队列
核心: 仅当有peek操作并且stk2为空的时候才将stk1中的内容放入stk2中
https://leetcode-cn.com/problems/implement-queue-using-stacks/
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 class MyQueue {private : stack <int > stk1, stk2;public : MyQueue() { } void push (int x) { stk1.push(x); } int pop () { int x = peek(); stk2.pop(); return x; } int peek () { if (stk2.size()) return stk2.top(); while (stk1.size()) { stk2.push(stk1.top()); stk1.pop(); } return stk2.top(); } bool empty () { return stk1.empty() && stk2.empty(); } };
31 | 二叉树的中序遍历 https://leetcode-cn.com/problems/binary-tree-inorder-traversal/
迭代写法 (morris暂时抛弃)
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 class Solution {public : vector <int > inorderTraversal (TreeNode* root) { vector <int > ans; stack <TreeNode*> stk; while (root || stk.size()) { while (root) { stk.push(root); root = root->left; } root = stk.top(); stk.pop(); ans.push_back(root->val); root = root->right; } return ans; } };
32 | 二叉树的右视图
bfs层序, 每次保存最后一个结点
https://leetcode-cn.com/problems/binary-tree-right-side-view/submissions/
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 class Solution {public : vector <int > rightSideView (TreeNode* root) { vector <int > ans; if (!root) return ans; queue <TreeNode*> q; q.push(root); while (q.size()) { int sz = q.size(); TreeNode* t = nullptr ; for (int i = 0 ; i < sz; ++i) { auto now = q.front(); q.pop(); t = now; if (now->left) q.push(now->left); if (now->right) q.push(now->right); } ans.push_back(t->val); } return ans; } };
33 | 爬楼梯 基本递推
1 2 3 4 5 6 7 8 9 10 11 class Solution {public : int climbStairs (int n) { vector <int > dp (n + 1 , 0 ) ; dp[0 ] = 1 , dp[1 ] = 1 ; for (int i = 2 ; i <= n; ++i) { dp[i] += dp[i - 1 ] + dp[i - 2 ]; } return dp[n]; } };
优化空间
1 2 3 4 5 6 7 8 9 10 11 12 13 class Solution {public : int climbStairs (int n) { int a = 1 , b = 1 ; int ans = 1 ; for (int i = 2 ; i <= n; ++i) { ans = a + b; a = b; b = ans; } return ans; } };
34 | 合并k个排序链表 https://leetcode-cn.com/problems/merge-k-sorted-lists/
归并排序优化空间
次数没变(k - 1次), 为什么归并比顺序合并空间优化?
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 class Solution {public : ListNode* mergeKLists (vector <ListNode*>& lists) { int n = lists.size(); return merge_list(lists, 0 , n - 1 ); } ListNode* merge_list (vector <ListNode*>& lists, int l, int r) { if (l > r) return nullptr ; if (l == r) return lists[r]; int mid = l + r >> 1 ; ListNode* l1 = merge_list(lists, l, mid); ListNode* l2 = merge_list(lists, mid + 1 , r); return merge(l1, l2); } ListNode* merge (ListNode* l1, ListNode* l2) { ListNode* dummy = new ListNode(0 , nullptr ); ListNode* now = dummy; while (l1 && l2) { if (l1->val < l2->val) { now->next = l1; l1 = l1->next; } else { now->next = l2; l2 = l2->next; } now = now->next; } if (l1) now->next = l1; if (l2) now->next = l2; return dummy->next; } };
优先队列写法
(不要一次性保存所有结点, 可以优化空间复杂度为O(k))
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 class Solution {public : ListNode* mergeKLists (vector <ListNode*>& lists) { priority_queue <pair <int , ListNode*>> q; for (auto i : lists) { if (i) q.emplace(-i->val, i); } ListNode* dummy = new ListNode(0 , nullptr ); ListNode* tail = dummy; while (q.size()) { auto [v, now] = q.top(); q.pop(); tail->next = now; tail = tail->next; if (now->next) q.emplace(-now->next->val, now->next); } return dummy->next; } };
35 | 链表中倒数第k个节点 https://leetcode-cn.com/problems/lian-biao-zhong-dao-shu-di-kge-jie-dian-lcof/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Solution {public : ListNode* getKthFromEnd (ListNode* head, int k) { ListNode* slow = head, *fast = head; for (int i = 0 ; i < k; ++i) fast = fast->next; while (fast) { fast = fast->next, slow = slow->next; } return slow; } };
36 | x的平方根 https://leetcode-cn.com/problems/sqrtx/submissions/
二分查找
注意爆int的问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Solution {public : int mySqrt (int x) { long long l = 0 , r = (long long )x + 1 ; while (l < r) { long long mid = (l + r) >> 1 ; if (mid * mid <= x) { l = mid + 1 ; } else { r = mid; } } return r - 1 ; } };
37 | 两数相加 https://leetcode-cn.com/problems/add-two-numbers/
减少特判! 和T18「字符串相加」的实现方法基本是一致的。
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 class Solution {public : ListNode* addTwoNumbers (ListNode* l1, ListNode* l2) { ListNode* dummy = new ListNode(0 , nullptr ); ListNode* now = dummy; int carry = 0 ; while (l1 || l2 || carry) { int a = 0 , b = 0 ; if (l1) { a += l1->val, l1 = l1->next; } if (l2) { b += l2->val, l2 = l2->next; } int res = a + b + carry; carry = res / 10 ; ListNode* new_node = new ListNode(res % 10 , nullptr ); now->next = new_node; now = now->next; } return dummy->next; } };
38 | 删除链表的倒数第 N 个结点 https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list/
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 class Solution {public : ListNode* removeNthFromEnd (ListNode* head, int n) { ListNode* dummy = new ListNode(0 , head); ListNode *slow = dummy, *fast = dummy; for (int i = 0 ; i < n; ++i) { fast = fast->next; } while (fast->next) { fast = fast->next, slow = slow->next; } slow->next = slow->next->next; return dummy->next; } };
39 | 字符串转整数 https://leetcode-cn.com/problems/string-to-integer-atoi/
小细节比较多
前导0
正负号
忽略其他部分
判断是否溢出 (简便方法直接上long long
)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Solution {public : int myAtoi (string s) { int p = 0 , len = s.size(); long long ans = 0 ; while (p < len && s[p] == ' ' ) ++p; if (p >= len) return 0 ; bool neg = false ; if (s[p] == '-' ) neg = true , ++p; else if (s[p] == '+' ) ++p; while (p < len && s[p] >= '0' && s[p] <= '9' ) { long long cur = s[p++] - '0' ; if (neg) cur = -cur; ans = ans * 10 + cur; if (ans > INT_MAX) return INT_MAX; if (ans < INT_MIN) return INT_MIN; } return static_cast <int >(ans); } };
40 | 重排链表 https://leetcode-cn.com/problems/reorder-list/
本题有两种做法:
双端队列
原地
双端队列很简单, 这里只记录原地的写法 :
快慢指针 找到链表中点将链表分开
反转后面的链表
按顺序合并链表
坑点:
快慢指针奇偶考虑清楚
分开后记得将l1末尾指向null
按顺序合并链表考虑l1和l2长度相等, 或者l1长度比l2多1的情况
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 class Solution {public : void reorderList (ListNode* head) { if (!head) return ; ListNode* l1 = head; ListNode* mid = split_list(head); ListNode* l2 = mid->next; mid->next = nullptr ; l2 = reverse_list(l2); merge(l1, l2); } ListNode* split_list (ListNode* head) { ListNode* slow = head, *fast = head; while (fast->next && fast->next->next) { fast = fast->next->next; slow = slow->next; } return slow; } ListNode* reverse_list (ListNode* head) { if (!head) return head; ListNode* pre = nullptr , *nex; while (head != nullptr ) { nex = head->next, head->next = pre, pre = head, head = nex; } return pre; } ListNode* merge (ListNode* l1, ListNode* l2) { ListNode* dummy = new ListNode(0 , nullptr ); ListNode* cur = dummy; while (l1 && l2) { cur->next = l1; l1 = l1->next; cur = cur->next; cur->next = l2; l2 = l2->next; cur = cur->next; } if (l1) cur->next = l1; return dummy->next; } };
41 | 合并区间 https://leetcode-cn.com/problems/merge-intervals/submissions/
画个数组想想就出来了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Solution {public : vector <vector <int >> merge(vector <vector <int >>& intervals) { int n = intervals.size(); sort(intervals.begin(), intervals.end()); vector <vector <int >> ans; int left = intervals[0 ][0 ], right = intervals[0 ][1 ]; for (auto inter : intervals) { int l = inter[0 ], r = inter[1 ]; if (right < l) { ans.push_back({left, right}); left = inter[0 ], right = inter[1 ]; } else { right = max(right, r); } } ans.push_back({left, right}); return ans; } };
42 | 二叉树的最大深度 https://leetcode-cn.com/problems/maximum-depth-of-binary-tree/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Solution {public : int maxDepth (TreeNode* root) { if (!root) return 0 ; return 1 + max(maxDepth(root->left), maxDepth(root->right)); } };
43 | 反转字符串里的单词 https://leetcode-cn.com/problems/reverse-words-in-a-string/
非O(1)写法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Solution {public : string reverseWords (string s) { stringstream ss (s) ; vector <string > ans; string x; while (ss >> x) { ans.push_back(x); } string res = "" ; reverse(ans.begin(), ans.end()); for (auto i : ans) { res += i + " " ; } res.pop_back(); return res; } };
O(1)写法
由于包含多个空格, 需要一个写指针记录写的位置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Solution {public : string reverseWords (string s) { int n = s.size(); reverse(s.begin(), s.end()); int p = 0 ; for (int l = 0 ; l < n; ++l) { if (s[l] != ' ' ) { if (p != 0 ) s[p++] = ' ' ; int r = l; while (r < n && s[r] != ' ' ) s[p++] = s[r++]; int len = r - l; reverse(s.begin() + p - len, s.begin() + p); l = r; } } s.erase(s.begin() + p, s.end()); return s; } };
44 | 平衡二叉树 https://leetcode-cn.com/problems/balanced-binary-tree/
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 class Solution {public : bool flag = true ; bool isBalanced (TreeNode* root) { check(root); return flag; } int check (TreeNode* root) { if (!root) return 0 ; int right_depth = check(root->right), left_depth = check(root->left); if (!flag) return -1 ; if (abs (right_depth - left_depth) > 1 ) flag = false ; return max(left_depth, right_depth) + 1 ; } };
45 | 二叉树的前序遍历 一样只写递归版本
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 class Solution {public : vector <int > preorderTraversal (TreeNode* root) { stack <TreeNode*> stk; vector <int > ans; while (root || stk.size()) { while (root) { stk.push(root); ans.push_back(root->val); root = root->left; } root = stk.top(); stk.pop(); root = root->right; } return ans; } };
46 | 二叉树中的最大路径和 https://leetcode-cn.com/problems/binary-tree-maximum-path-sum/submissions/
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 class Solution {public : int ans = INT_MIN; int maxPathSum (TreeNode* root) { check(root); return ans; } int check (TreeNode* root) { if (!root) return 0 ; int l = check(root->left); int r = check(root->right); l = max(0 , l), r = max(0 , r); ans = max(ans, l + r + root->val); return max(l, r) + root->val; } };
47 | 路径总和2 https://leetcode-cn.com/problems/path-sum-ii/submissions/
减弱版为:「64. 路经总和」(不需要记录路径)
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 class Solution {public : vector <vector <int >> ans; int target; void dfs (TreeNode* root, vector <int >& cur, int sum) { if (!root) return ; cur.push_back(root->val); sum += root->val; if (!root->right && !root->left && target == sum) { ans.push_back(cur); } dfs(root->left, cur, sum); dfs(root->right, cur, sum); cur.pop_back(); } vector <vector <int >> pathSum(TreeNode* root, int targetSum) { target = targetSum; vector <int > now; dfs(root, now, 0 ); return ans; } };
48 | 下一个排列 https://leetcode-cn.com/problems/next-permutation/submissions/
(官方题解好像要短点, 思路是一样的)
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 class Solution {public : void nextPermutation (vector <int >& nums) { int n = nums.size(); bool flag = false ; for (int k = n - 2 ; k >= 0 ; --k) { if (nums[k] < nums[k + 1 ]) { flag = true ; for (int j = n - 1 ; j > k; --j) { if (nums[j] > nums[k]) { swap(nums[j], nums[k]); reverse(nums.begin() + k + 1 , nums.end()); break ; } } } if (flag) return ; } if (!flag) reverse(nums.begin(), nums.end()); } };
49 | 从前序与中序遍历序列构造二叉树 https://leetcode-cn.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/
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 class Solution {public : int ptr = 0 , n = 0 ; unordered_map <int , int > idx; TreeNode* buildTree (vector <int >& preorder, vector <int >& inorder) { n = preorder.size(); for (int i = 0 ; i < n; ++i) { idx[inorder[i]] = i; } return build(0 , n - 1 , preorder, inorder); } TreeNode* build (int left, int right, vector <int >& preorder, vector <int >& inorder) { if (left > right) return nullptr ; int now = preorder[ptr++]; TreeNode* node = new TreeNode(now); int mid = idx[now]; node->left = build(left, mid - 1 , preorder, inorder); node->right = build(mid + 1 , right, preorder, inorder); return node; } };
50 | 排序链表 自顶向下归并排序
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 class Solution {public : ListNode* sortList (ListNode* head) { if (!head) return head; ListNode* t = head; while (t->next) t = t->next; return sortList(head, t); } ListNode* find_mid (ListNode* head) { ListNode* slow = head, *fast = head; while (fast->next && fast->next->next) { fast = fast->next->next; slow = slow->next; } return slow; } ListNode* sortList (ListNode* head, ListNode* tail) { if (head == tail) return head; ListNode* mid = find_mid(head); ListNode* nex = mid->next; mid->next = nullptr ; return merge(sortList(head, mid), sortList(nex, tail)); } ListNode* merge (ListNode *l1, ListNode* l2) { ListNode* dummy = new ListNode(0 , nullptr ), *now = dummy; while (l1 && l2) { if (l1->val < l2->val) { now->next = l1; l1 = l1->next; } else { now->next = l2; l2 = l2->next; } now = now->next; } if (l1) now->next = l1; if (l2) now->next = l2; return dummy->next; } };
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 class Solution {public : ListNode* sortList (ListNode* head) { if (!head) return head; int length = 0 ; ListNode* tmp = head; while (tmp) tmp = tmp->next, ++length; ListNode* dummy = new ListNode(0 , head); for (int len = 1 ; len < length; len <<= 1 ) { ListNode* prev = dummy, *now = dummy->next; while (now) { ListNode* left = now; for (int i = 0 ; i < len - 1 && now->next; ++i) { now = now->next; } ListNode* right = now->next; now->next = nullptr ; now = right; for (int i = 0 ; i < len - 1 && now && now->next; ++i) { now = now->next; } ListNode* remain = now ? now->next : nullptr ; if (now) now->next = nullptr ; prev->next = merge(left, right); while (prev->next) prev = prev->next; now = remain; } } return dummy->next; } ListNode* merge (ListNode *l1, ListNode* l2) { ListNode* dummy = new ListNode(0 , nullptr ), *now = dummy; while (l1 && l2) { if (l1->val < l2->val) { now->next = l1; l1 = l1->next; } else { now->next = l2; l2 = l2->next; } now = now->next; } if (l1) now->next = l1; if (l2) now->next = l2; return dummy->next; } };
51 | 求根节点到叶节点数字之和 https://leetcode-cn.com/problems/sum-root-to-leaf-numbers/
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 class Solution {public : int sum = 0 ; int sumNumbers (TreeNode* root) { dfs(root, 0 ); return sum; } void dfs (TreeNode* root, int now) { if (!root) return ; now = now * 10 + root->val; if (!root->left && !root->right) { sum += now; } dfs(root->left, now); dfs(root->right, now); } };
52 | 用 Rand7() 实现 Rand10() https://leetcode-cn.com/problems/implement-rand10-using-rand7/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Solution {public : int rand10 () { int res; do { res = (rand7() - 1 ) * 7 + rand7(); } while (res > 40 ); return (res - 1 ) % 10 + 1 ; } };
53 | 编辑距离 https://leetcode-cn.com/problems/edit-distance/
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 class Solution {public : int minDistance (string word1, string word2) { int n = word1.size(), m = word2.size(); vector <vector <int >> dp(n + 1 , vector <int > (m + 1 , 0 )); for (int i = 0 ; i <= n; ++i) dp[i][0 ] = i; for (int j = 0 ; j <= m; ++j) dp[0 ][j] = j; for (int i = 1 ; i <= n; ++i) { for (int j = 1 ; j <= m; ++j) { int a = dp[i - 1 ][j - 1 ], b = dp[i - 1 ][j], c = dp[i][j - 1 ]; if (word1[i - 1 ] == word2[j - 1 ]) { dp[i][j] = min(a, min(b, c) + 1 ); } else { dp[i][j] = min(a, min(b, c)) + 1 ; } } } return dp[n][m]; } };
54 | 寻找两个正序数组的中位数
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 class Solution {public : int find_kth_element (vector <int >& nums1, vector <int >& nums2, int k) { int n = nums1.size(), m = nums2.size(); int idx1 = 0 , idx2 = 0 ; while (1 ) { if (idx1 == n) { return nums2[idx2 + k - 1 ]; } if (idx2 == m) { return nums1[idx1 + k - 1 ]; } if (k == 1 ) { return min(nums1[idx1], nums2[idx2]); } int new_idx1 = min(idx1 + k / 2 - 1 , n - 1 ); int new_idx2 = min(idx2 + k / 2 - 1 , m - 1 ); int p1 = nums1[new_idx1], p2 = nums2[new_idx2]; if (p1 <= p2) { k -= new_idx1 - idx1 + 1 ; idx1 = new_idx1 + 1 ; } else { k -= new_idx2 - idx2 + 1 ; idx2 = new_idx2 + 1 ; } } } double findMedianSortedArrays (vector <int >& nums1, vector <int >& nums2) { int len = nums1.size() + nums2.size(); if (len & 1 ) { return static_cast <double > (find_kth_element(nums1, nums2, len / 2 + 1 )); } else { double left = static_cast <double > (find_kth_element(nums1, nums2, len / 2 + 1 )); double right = static_cast <double > (find_kth_element(nums1, nums2, len / 2 )); return (left + right) / 2.000 ; } } };
55 | 最小覆盖子串 经典滑动窗口, 注意两个哈希表的用途
这里c
在第一次找到后就不变了, 之后一直靠j
维护count和source的冗余关系来维持窗口
https://leetcode-cn.com/problems/minimum-window-substring/submissions/
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 class Solution {public : unordered_map <char , int > count, source; string minWindow (string s, string t) { for (auto i : t) source[i]++; string ans; int c = 0 ; for (int i = 0 , j = 0 ; i < s.size(); ++i) { ++count[s[i]]; if (count[s[i]] <= source[s[i]]) c++; while (j <= i && count[s[j]] > source[s[j]]) --count[s[j++]]; if (c == t.size()) { int len = i - j + 1 ; if (ans.empty() || len < ans.size()) { ans = s.substr(j, len); } } } return ans; } };
56 | 删除链表中的重复元素2 https://leetcode-cn.com/problems/remove-duplicates-from-sorted-list-ii/
是「74 | 删除排序链表中的重复元素」的加强版
用一个指在now前面的指针跑重复元素即可
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 class Solution {public : ListNode* deleteDuplicates (ListNode* head) { if (!head) return head; ListNode* dummy = new ListNode(0 , head); ListNode* now = dummy; while (now->next && now->next->next) { ListNode* p = now->next; if (p->val == p->next->val) { while (p->next && p->val == p->next->val) p = p->next; now->next = p->next; } else { now = now->next; } } return dummy->next; } };
57 | 缺失的第一个正数 https://leetcode-cn.com/problems/first-missing-positive/
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 class Solution {public : int firstMissingPositive (vector <int >& nums) { int n = nums.size(); for (auto & i : nums) { if (i <= 0 ) i = n + 1 ; } for (auto & i : nums) { int k = abs (i); if (k <= n) nums[k - 1 ] = -abs (nums[k - 1 ]); } for (int i = 0 ; i < n; ++i) { if (nums[i] > 0 ) return i + 1 ; } return n + 1 ; } };
58 | 滑动窗口最大值 https://leetcode-cn.com/problems/sliding-window-maximum/submissions/
单调队列 => 双端队列
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Solution {public : vector <int > maxSlidingWindow (vector <int >& nums, int k) { deque <int > q; vector <int > ans; for (int i = 0 ; i < nums.size(); ++i) { while (i - k + 1 >= 0 && q.size() && q.front() < i - k + 1 ) { q.pop_front(); } while (q.size() && nums[i] > nums[q.back()]) q.pop_back(); q.push_back(i); if (i >= k - 1 ) ans.push_back(nums[q.front()]); } return ans; } };
59 | 二叉树的直径 https://leetcode-cn.com/problems/diameter-of-binary-tree/
和那个二叉树最大路径和 基本一致, 为啥这题是简单那题是困难?
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 class Solution {public : int ans = 0 ; int diameterOfBinaryTree (TreeNode* root) { dfs(root); return ans; } int dfs (TreeNode* root) { if (!root) return 0 ; int l = dfs(root->left), r = dfs(root->right); ans = max(ans, l + r); return max(l, r) + 1 ; } };
60 | 最小栈 两个栈, 保存最小值的栈「同步更新」
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 class MinStack {public : stack <int > stk, min_stk; MinStack() { } void push (int val) { stk.push(val); if (min_stk.empty() || val < min_stk.top()) min_stk.push(val); else min_stk.push(min_stk.top()); } void pop () { stk.pop(); min_stk.pop(); } int top () { return stk.top(); } int getMin () { return min_stk.top(); } };
61 | 最长重复子数组 https://leetcode-cn.com/problems/maximum-length-of-repeated-subarray/
动态规划
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Solution {public : static const int N = 1005 ; int dp[N][N]; int findLength (vector <int >& nums1, vector <int >& nums2) { int n = nums1.size(), m = nums2.size(); int ans = 0 ; for (int i = 1 ; i <= n; ++i) { for (int j = 1 ; j <= m; ++j) { if (nums1[i - 1 ] == nums2[j - 1 ]) { dp[i][j] = max(dp[i - 1 ][j - 1 ] + 1 , dp[i][j]); ans = max(ans, dp[i][j]); } } } return ans; } };
滑动窗口
理解下两种偏移方式
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 class Solution {public : int count (vector <int >& nums1, vector <int >& nums2, int offset1, int offset2, int len) { int cnt = 0 , ans = 0 ; for (int i = 0 ; i < len; ++i) { if (nums1[offset1 + i] == nums2[offset2 + i]) { ++cnt; } else { cnt = 0 ; } ans = max(ans, cnt); } return ans; } int findLength (vector <int >& nums1, vector <int >& nums2) { int n = nums1.size(), m = nums2.size(); int ans = 0 ; for (int i = 0 ; i < n; ++i) { int len = min(n - i, m); ans = max(ans, count(nums1, nums2, i, 0 , len)); } for (int i = 0 ; i < m; ++i) { int len = min(m - i, n); ans = max(ans, count(nums1, nums2, 0 , i, len)); } return ans; } };
62 | 最长公共子序列 https://leetcode-cn.com/problems/longest-common-subsequence/
动态规划, 注意子序列和子数组不同
子数组的dp[i][j]
表示的是以i, j
结尾的
子序列的dp[i][j]
表示的是前i个, 前j个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Solution {public : static const int N = 1005 ; int dp[N][N]; int longestCommonSubsequence (string s1, string s2) { int n = s1.size(), m = s2.size(); for (int i = 1 ; i <= n; ++i) { for (int j = 1 ; j <= m; ++j) { dp[i][j] = max(dp[i - 1 ][j], dp[i][j - 1 ]); if (s1[i - 1 ] == s2[j - 1 ]) dp[i][j] = max(dp[i][j], dp[i - 1 ][j - 1 ] + 1 ); } } return dp[n][m]; } };
63 | 回文链表 https://leetcode-cn.com/problems/palindrome-linked-list/
快慢指针找中点
反转后面链表
挨个比对(以后面链表为边界, 前面长度可能会比后面多1, 但是无妨)
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 class Solution {public : bool isPalindrome (ListNode* head) { if (!head) return false ; ListNode* slow = head, *fast = head; while (fast->next && fast->next->next) { slow = slow->next; fast = fast->next->next; } ListNode* head2 = slow->next; slow->next = nullptr ; auto reverse_list = [](ListNode* h) { ListNode* nex = nullptr , *prev = nullptr ; while (h) { nex = h->next, h->next = prev, prev = h, h = nex; } return prev; }; head2 = reverse_list(head2); while (head2) { if (head->val != head2->val) return false ; head = head->next, head2 = head2->next; } return true ; } };
64 | 路经总和 https://leetcode-cn.com/problems/path-sum/
加强版为「47. 路经总和2」
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 class Solution {public : bool flag = false ; int tar; void dfs (TreeNode* root, int sum) { if (!root) return ; sum += root->val; if (!root->left && !root->right) { if (sum == tar) { flag = true ; } return ; } dfs(root->left, sum), dfs(root->right, sum); } bool hasPathSum (TreeNode* root, int targetSum) { tar = targetSum; dfs(root, 0 ); return flag; } };
65 | 多数元素 https://leetcode-cn.com/problems/majority-element/submissions/
摩尔投票法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Solution {public : int majorityElement (vector <int >& nums) { int candidate = 3 ; int count = 0 ; for (int num : nums) { if (num == candidate) ++count; else if (--count < 0 ) { candidate = num; count = 1 ; } } return candidate; } };
66 | 验证二叉搜索树 https://leetcode-cn.com/problems/validate-binary-search-tree/
中序遍历递归
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 class Solution {public : long long pre = LLONG_MIN; bool flag = true ; void dfs (TreeNode* root) { if (!root) return ; dfs(root->left); if (root->val <= pre) { flag = false ; return ; } pre = root->val; dfs(root->right); } bool isValidBST (TreeNode* root) { dfs(root); return flag; } };
中序遍历迭代
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 class Solution {public : bool isValidBST (TreeNode* root) { stack <TreeNode*> stk; long long pre = LLONG_MIN; while (root || stk.size()) { while (root) { stk.push(root); root = root->left; } root = stk.top(); stk.pop(); if (root->val <= pre) return false ; pre = root->val; root = root->right; } return true ; } };
67 | 旋转图像 暴力的做法:
第一行 放到 最后一列
1 2 3 4 5 6 7 8 9 10 11 12 13 class Solution {public : void rotate (vector <vector <int >>& matrix) { int n = matrix.size(); vector <vector <int >> help(n, vector (n, 0 )); for (int i = 0 ; i < n; ++i) { for (int j = 0 ; j < n; ++j) { help[j][n - 1 - i] = matrix[i][j]; } } matrix = move(help); } };
反转的做法:
上下翻转
沿主对角线翻转
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Solution {public : void rotate (vector <vector <int >>& matrix) { int n = matrix.size(); for (int i = 0 ; i < n / 2 ; ++i) { for (int j = 0 ; j < n; ++j) { swap(matrix[i][j], matrix[n - 1 - i][j]); } } for (int i = 0 ; i < n; ++i) { for (int j = i + 1 ; j < n; ++j) { swap(matrix[i][j], matrix[j][i]); } } } };
68 | 对称二叉树 https://leetcode-cn.com/problems/symmetric-tree/submissions/
递归写法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Solution {public : bool check (TreeNode* a, TreeNode* b) { if (!a && !b) return true ; if (a && !b || !a && b) return false ; return (a->val == b->val && check(a->left, b->right) && check(a->right, b->left)); } bool isSymmetric (TreeNode* root) { return check(root, root); } };
迭代写法
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 class Solution {public : bool check (TreeNode* a, TreeNode* b) { queue <TreeNode*> q; q.push(a), q.push(b); while (q.size()) { auto u = q.front(); q.pop(); auto v = q.front(); q.pop(); if (!u && !v) continue ; if (!u && v || !v && u) return false ; if (u->val != v->val) return false ; q.push(u->left), q.push(v->right); q.push(u->right), q.push(v->left); } return true ; } bool isSymmetric (TreeNode* root) { return check(root, root); } };
69 | 子集 二进制枚举法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Solution {public : vector <vector <int >> subsets(vector <int >& nums) { int n = nums.size(); vector <vector <int >> ans; for (int i = 0 ; i < (1 << n); ++i) { vector <int > now; for (int j = 0 ; j < n; ++j) { if (i >> j & 1 ) { now.push_back(nums[j]); } } ans.push_back(now); } return ans; } };
回溯
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Solution {public : vector <vector <int >> ans; vector <int > cur; void dfs (vector <int >& nums, int now) { if (now == nums.size()) { ans.push_back(cur); return ; } cur.push_back(nums[now]); dfs(nums, now + 1 ); cur.pop_back(); dfs(nums, now + 1 ); } vector <vector <int >> subsets(vector <int >& nums) { dfs(nums, 0 ); return ans; } };
70 | 翻转二叉树 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Solution {public : TreeNode* invertTree (TreeNode* root) { if (!root) return root; TreeNode* tmp = root->left; root->left = root->right, root->right = tmp; invertTree(root->left), invertTree(root->right); return root; } };
71 | 复原ip地址 回溯
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 class Solution {public : vector <string > ans; vector <string > restoreIpAddresses (string s) { if (s.size() > 12 ) return {}; dfs(s, "" , 0 ); return ans; } void dfs (string & s, string cur, int start) { if (cur.size() == s.size() + 3 ) { if (start == s.size()) ans.push_back(cur); return ; } string now = "" ; for (int i = start; i < min((int )s.size(), start + 3 ); ++i) { now += s[i]; if (valid(now)) { dfs(s, cur.size() ? cur + "." + now : now, i + 1 ); } } } bool valid (string & x) { if (x.size() < 1 || x.size() > 3 ) return false ; if (x.size() > 1 && x[0 ] == '0' ) return false ; int num = stoi(x); return num >= 0 && num <= 255 ; }; };
72 | 零钱兑换 典型的完全背包问题 (物品可以选择无数次)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Solution {public : const int inf = 0x3f3f3f3f ; int coinChange (vector <int >& coins, int amount) { vector <int > dp (amount + 1 , inf) ; dp[0 ] = 0 ; for (int i = 0 ; i <= amount; ++i) { for (auto j : coins) { if (i - j >= 0 ) { dp[i] = min(dp[i], dp[i - j] + 1 ); } } } return dp[amount] == inf ? -1 : dp[amount]; } };
关于背包
关于初始化:
恰好装满 ==> dp[i] = inf
小于等于 ==> dp[i] = 0
0-1背包
有 N 件物品和一个容量是 V的背包。每件物品只能使用一次。
第 i 件物品的体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include <bits/stdc++.h> using namespace std ;int f[1005 ];int w[1005 ], v[1005 ];int main () { int N, V; cin >> N >> V; for (int i = 0 ; i < N; ++i) { cin >> v[i] >> w[i]; } for (int i = 0 ; i < N; ++i) { for (int j = V; j >= v[i]; --j) { f[j] = max(f[j], f[j - v[i]] + w[i]); } } cout << f[V] << endl ; return 0 ; }
完全背包和0-1背包相比仅仅是第二层循环的顺序改了
完全背包
有 N 件物品和一个容量是 V的背包。每种物品都有无限件可用。
第 i 件物品的体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #include <bits/stdc++.h> using namespace std ;int f[1005 ];int v[1005 ], w[1005 ];int main () { int N, V; cin >> N >> V; for (int i = 0 ; i < N; ++i) { cin >> v[i] >> w[i]; } for (int j = 0 ; j <= V; ++j) { for (int i = 0 ; i < N; ++i) { if (j - v[i] >= 0 ) { f[j] = max(f[j], f[j - v[i]] + w[i]); } } } cout << f[V] << endl ; return 0 ; }
多重背包
有 N 种物品和一个容量是 V 的背包。
第 i 种物品最多有 si 件,每件体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #include <bits/stdc++.h> using namespace std ;int f[1005 ];int s[1005 ], v[1005 ], w[1005 ];int main () { int N, V; cin >> N >> V; for (int i = 0 ; i < N; ++i) { cin >> v[i] >> w[i] >> s[i]; } for (int i = 0 ; i < N; ++i) { for (int j = V; j >= 0 ; --j) { for (int k = 1 ; k <= s[i] && k * v[i] <= j; ++k) { f[j] = max(f[j], f[j - k * v[i]] + k * w[i]); } } } cout << f[V] << endl ; return 0 ; }
多重背包+二进制优化
题目同上, 但是由于多重背包复杂度N V S, 当数量比较大时会超, 故采用二进制优化的方式将多重背包转化成01背包的问题
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 #include <bits/stdc++.h> using namespace std ;const int N = 2010 ;int f[N];int main () { int nn, V; int v, w, s; cin >> nn >> V; vector <pair <int , int >> all; for (int i = 0 ; i < nn; ++i) { cin >> v >> w >> s; for (int k = 1 ; k <= s; k <<= 1 ) { s -= k; all.emplace_back(v * k, w * k); } if (s > 0 ) { all.emplace_back(v * s, w * s); } } for (auto & [v, w] : all) { for (int j = V; j - v >= 0 ; --j) { f[j] = max(f[j], f[j - v] + w); } } cout << f[V] << endl ; return 0 ; }
混合背包
有 N 种物品和一个容量是 V 的背包。
物品一共有三类:
第一类物品只能用1次(01背包);
第二类物品可以用无限次(完全背包);
第三类物品最多只能用 si次(多重背包);
每种体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。 输出最大价值。
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 #include <bits/stdc++.h> using namespace std ;vector <tuple<int , int , int >> all; int f[1005 ];int main () { int N, V; cin >> N >> V; for (int i = 0 ; i < N; ++i) { int v, w, t; cin >> v >> w >> t; if (t == -1 ) { all.emplace_back(0 , v, w); } else if (t == 0 ) { all.emplace_back(-1 , v, w); } else { for (int k = 1 ; k <= t; k <<= 1 ) { t -= k; all.emplace_back(0 , v * k, w * k); } if (t > 0 ) { all.emplace_back(0 , v * t, w * t); } } } for (auto & [t, v, w] : all) { if (t == 0 ) { for (int i = V; i - v >= 0 ; --i) { f[i] = max(f[i], f[i - v] + w); } } else if (t == -1 ) { for (int i = v; i <= V; ++i) { f[i] = max(f[i], f[i - v] + w); } } } cout << f[V] << endl ; return 0 ; }
二维费用背包
就是把0-1背包的限制条件扩展到了二维, 思路是一致的
有 N 件物品和一个容量是 V 的背包,背包能承受的最大重量是 M。
每件物品只能用一次。体积是 vi,重量是 mi,价值是 wi。
求解将哪些物品装入背包,可使物品总体积不超过背包容量,总重量不超过背包可承受的最大重量,且价值总和最大。
输出最大价值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #include <bits/stdc++.h> using namespace std ;const int maxn = 1005 ;int f[maxn][maxn];int main () { int N, V, M; cin >> N >> V >> M; int v, m, w; for (int i = 0 ; i < N; ++i) { cin >> v >> m >> w; for (int i = V; i - v >= 0 ; --i) { for (int j = M; j - m >= 0 ; --j) { f[i][j] = max(f[i][j], f[i - v][j - m] + w); } } } cout << f[V][M]; return 0 ; }
分组背包问题
有 N 组物品和一个容量是 V 的背包。
每组物品有若干个,同一组内的物品最多只能选一个。 每件物品的体积是 vij,价值是 wij,其中 i 是组号,j是组内编号。
求解将哪些物品装入背包,可使物品总体积不超过背包容量,且总价值最大。
输出最大价值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #include <bits/stdc++.h> using namespace std ;int f[105 ], v[105 ], w[105 ];int main () { int N, V; cin >> N >> V; int s; while (N--) { cin >> s; for (int i = 0 ; i < s; ++i) { cin >> v[i] >> w[i]; } for (int i = V; i >= 0 ; --i) { for (int j = 0 ; j < s; ++j) { if (i - v[j] >= 0 ) f[i] = max(f[i], f[i - v[j]] + w[j]); } } } cout << f[V] << endl ; return 0 ; }
73 | 在排序数组中查找元素的第一个和最后一个位置
https://leetcode-cn.com/problems/find-first-and-last-position-of-element-in-sorted-array/
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 class Solution {public : vector <int > searchRange (vector <int >& nums, int target) { int n = nums.size(); int l = 0 , r = n; while (l < r) { int mid = l + r >> 1 ; if (nums[mid] <= target) { l = mid + 1 ; } else { r = mid; } } int up = r; l = 0 , r = n; while (l < r) { int mid = l + r >> 1 ; if (nums[mid] < target) { l = mid + 1 ; } else { r = mid; } } int lo = r; if (up > lo) return {lo, up - 1 }; else return {-1 , -1 }; } };
74 | 删除排序链表中的重复元素 是「56 | 删除链表中的重复元素2」的简化版
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 class Solution {public : ListNode* deleteDuplicates (ListNode* head) { if (!head) return head; ListNode* dummy = new ListNode(0 , head); ListNode* now = dummy; while (now->next) { ListNode* p = now->next; if (p->next && p->val == p->next->val) { while (p->next && p->val == p->next->val) { p = p->next; } } now->next = p; now = now->next; } return dummy->next; } };
75 | 比较版本号 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Solution {public : int compareVersion (string version1, string version2) { int num1 = 0 , num2 = 0 ; int n1 = version1.size(), n2 = version2.size(); int p1 = 0 , p2 = 0 ; while (p1 < n1 || p2 < n2) { int t1 = 0 , t2 = 0 ; while (p1 < n1 && version1[p1] != '.' ) { t1 = t1 * 10 + version1[p1++] - '0' ; } ++p1; while (p2 < n2 && version2[p2] != '.' ) { t2 = t2 * 10 + version2[p2++] - '0' ; } ++p2; if (t1 != t2) { return t1 < t2 ? -1 : 1 ; } } return 0 ; } };
76 | 最小路径和 https://leetcode-cn.com/problems/minimum-path-sum/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Solution {public : int minPathSum (vector <vector <int >>& grid) { int n = grid.size(), m = grid[0 ].size(); vector <int > dp (m, INT_MAX) ; dp[0 ] = grid[0 ][0 ]; for (int i = 0 ; i < n; ++i) { for (int j = 0 ; j < m; ++j) { if (i != 0 ) dp[j] += grid[i][j]; if (j != 0 ) dp[j] = min(dp[j], dp[j - 1 ] + grid[i][j]); } } return dp[m - 1 ]; } };
77 | 只出现一次的数字 https://leetcode-cn.com/problems/single-number/
1 2 3 4 5 6 7 8 class Solution {public : int singleNumber (vector <int >& nums) { int ans = 0 ; for (auto & i : nums) ans ^= i; return ans; } };
78 | 括号生成 https://leetcode-cn.com/problems/generate-parentheses/
二进制枚举, 无优化, 铁暴力
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 class Solution {public : bool check (string s) { int cnt = 0 ; for (auto & i : s) { if (i == '(' ) ++cnt; if (i == ')' ) { if (cnt <= 0 ) return false ; --cnt; } } return cnt == 0 ; } vector <string > generateParenthesis (int n) { n <<= 1 ; vector <string > ans; for (int i = 0 ; i < (1 << n); ++i) { string now = "" ; for (int j = 0 ; j < n; ++j) { if (i >> j & 1 ) { now += '(' ; } else { now += ')' ; } } if (check(now)) ans.push_back(now); } return ans; } };
回溯带优化
左 + 右 = n终止
右 > 左 break
左 = 右 放左
左 > 右 放左, 右
回溯
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Solution {public : vector <string > ans; vector <string > generateParenthesis (int n) { dfs("" , n, 0 , 0 ); return ans; } void dfs (string now, int n, int l, int r) { if (l + r == n * 2 ) { if (l == r) ans.push_back(now); return ; } if (r > l) return ; if (r <= l) dfs(now + '(' , n, l + 1 , r); dfs(now + ')' , n, l, r + 1 ); } };
79 |寻找旋转排序数组中的最小值 https://leetcode-cn.com/problems/find-minimum-in-rotated-sorted-array/
这个二分不太正常,既没有左闭右开,右边界又没有收缩到n - 1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Solution {public : int findMin (vector <int >& nums) { int n = nums.size(); int l = 0 , r = n - 1 ; while (l < r) { int mid = l + r >> 1 ; if (nums[mid] < nums[n - 1 ]) { r = mid; } else { l = mid + 1 ; } } return nums[r]; } };
80 | 组合总和 https://leetcode-cn.com/problems/combination-sum/
一开始想的方法是枚举每个数被选取(1, 2, 3, … k)次, 直到sum + k * now >= target
但注意到这样会让now有很多份, 因为无法及时pop
掉
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 class Solution {public : vector <vector <int >> ans; void dfs (int idx, vector <int >& candidates, int target, vector <int > now, int sum) { if (sum == target) { ans.push_back(now); return ; } if (idx >= candidates.size() || sum > target) return ; int k = 0 ; while (k * candidates[idx] + sum <= target) { if (k) now.push_back(candidates[idx]); dfs(idx + 1 , candidates, target, now, sum + k * candidates[idx]); ++k; } } vector <vector <int >> combinationSum(vector <int >& candidates, int target) { vector <int > now; dfs(0 , candidates, target, now, 0 ); return ans; } };
考虑到可以直接不进行下标递增的选取, 在原地表示(跳过或者不跳过), 可以优化空间
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 class Solution {public : vector <vector <int >> ans; void dfs (int idx, vector <int >& candidates, int target, vector <int >& now) { if (idx >= candidates.size()) return ; if (target == 0 ) { ans.push_back(now); return ; } dfs(idx + 1 , candidates, target, now); if (target - candidates[idx] >= 0 ) { now.push_back(candidates[idx]); dfs(idx, candidates, target - candidates[idx], now); now.pop_back(); } } vector <vector <int >> combinationSum(vector <int >& candidates, int target) { vector <int > now; dfs(0 , candidates, target, now); return ans; } };
81 | 字符串相乘 https://leetcode-cn.com/problems/multiply-strings/
直接用数组保存, 然后对数组进行「竖式加法」
相当于模拟乘法竖式的时候将乘法时候的「进位」保留了, 最后相加时候一起进位
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 class Solution {public : string multiply (string num1, string num2) { reverse(num1.begin(), num1.end()); reverse(num2.begin(), num2.end()); int n = num1.size(), m = num2.size(); vector <int > c (n + m, 0 ) ; int carry = 0 ; for (int i = 0 ; i < n; ++i) { int a = num1[i] - '0' ; for (int j = 0 ; j < m; ++j) { int b = num2[j] - '0' ; c[i + j] += a * b; } } for (int i = 0 , t = 0 ; i < n + m; ++i) { t += c[i]; c[i] = t % 10 ; t /= 10 ; } while (c.size() > 1 && c.back() == 0 ) c.pop_back(); string ans; for (int i = c.size() - 1 ; i >= 0 ; --i) { ans += (c[i] + '0' ); } return ans; } };
82 | 调整数组顺序使奇数位于偶数前面 https://leetcode-cn.com/problems/diao-zheng-shu-zu-shun-xu-shi-qi-shu-wei-yu-ou-shu-qian-mian-lcof/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Solution {public : vector <int > exchange (vector <int >& nums) { int p1 = 0 , p2 = nums.size() - 1 ; while (p1 < p2) { while (p1 < p2 && nums[p1] % 2 == 1 ) ++p1; while (p1 < p2 && nums[p2] % 2 == 0 ) --p2; if (p1 < p2) swap(nums[p1], nums[p2]); } return nums; } };
83 | 二叉树的后序遍历 假的后序, 真的前序写法:
前序 中->右->左
翻转后成为左->右->中即为后序
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 class Solution {public : vector <int > postorderTraversal (TreeNode* root) { stack <TreeNode*> stk; vector <int > ans; while (root || stk.size()) { while (root) { ans.push_back(root->val); stk.push(root); root = root->right; } root = stk.top(); stk.pop(); root = root->left; } reverse(ans.begin(), ans.end()); return ans; } };
真的后序写法(需要记录前一个结点, 防止重复访问右边)
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 class Solution {public : vector <int > postorderTraversal (TreeNode* root) { stack <TreeNode*> stk; vector <int > ans; TreeNode* prev = nullptr ; while (root || stk.size()) { while (root) { stk.push(root); root = root->left; } root = stk.top(); stk.pop(); if (!root->right || root->right == prev) { ans.push_back(root->val); prev = root; root = nullptr ; } else { stk.push(root); root = root->right; } } return ans; } };
84 | 对角线遍历 做这种题最重要的是:
找规律
充分讨论边界情况
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 class Solution {public : vector <int > findDiagonalOrder (vector <vector <int >>& mat) { vector <int > ans; int row = mat.size(), col = mat[0 ].size(); int all = row * col; int x = 0 , y = 0 ; for (int i = 0 ; i < all; ++i) { ans.push_back(mat[x][y]); if ((x + y) % 2 == 0 ) { if (y == col - 1 ) ++x; else if (x == 0 ) ++y; else --x, ++y; } else { if (x == row - 1 ) ++y; else if (y == 0 ) ++x; else ++x, --y; } } return ans; } };
85 | 斐波那契数列 https://leetcode-cn.com/problems/fei-bo-na-qi-shu-lie-lcof/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Solution {public : int fib (int n) { int a = 0 , b = 1 ; int mod = (int ) 1e9 + 7 ; if (n == 0 ) return a; if (n == 1 ) return b; int ans = 0 ; for (int i = 2 ; i <= n; ++i) { ans = a + b; ans %= mod; a = b, b = ans; } return ans; } };
86 | 复制带随机指针的链表 https://leetcode-cn.com/problems/copy-list-with-random-pointer/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Solution {public : unordered_map <Node*, Node*> mem; Node* copyRandomList (Node* head) { if (!head) return head; if (!mem.count(head)) { Node* new_node = new Node(head->val); mem[head] = new_node; new_node->next = copyRandomList(head->next); new_node->random = copyRandomList(head->random); } return mem[head]; } };
87 | 买卖股票的最佳时机2 https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/
由于不限交易次数, 我们将所有上升段统计即可
1 2 3 4 5 6 7 8 9 10 11 12 13 class Solution {public : int maxProfit (vector <int >& prices) { int ans = 0 ; for (int i = 1 ; i < prices.size(); ++i) { ans += max(0 , prices[i] - prices[i - 1 ]); } return ans; } };
dp的操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Solution {public : int maxProfit (vector <int >& prices) { vector <vector <int >> dp(prices.size(), vector <int >(2 , 0 )); dp[0 ][0 ] = 0 , dp[0 ][1 ] = -prices[0 ]; for (int i = 1 ; i < prices.size(); ++i) { dp[i][0 ] = max(dp[i - 1 ][0 ], dp[i - 1 ][1 ] + prices[i]); dp[i][1 ] = max(dp[i - 1 ][1 ], dp[i - 1 ][0 ] - prices[i]); } return dp[prices.size() - 1 ][0 ]; } };
加上滚动数组优化的dp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Solution {public : int maxProfit (vector <int >& prices) { vector <vector <int >> dp(2 , vector <int >(2 , 0 )); dp[0 ][0 ] = 0 , dp[0 ][1 ] = -prices[0 ]; for (int i = 1 ; i < prices.size(); ++i) { dp[i & 1 ][0 ] = max(dp[(i - 1 ) & 1 ][0 ], dp[(i - 1 ) & 1 ][1 ] + prices[i]); dp[i & 1 ][1 ] = max(dp[(i - 1 ) & 1 ][1 ], dp[(i - 1 ) & 1 ][0 ] - prices[i]); } return dp[(prices.size() - 1 ) & 1 ][0 ]; } };
88 | 数组中的逆序对 https://leetcode-cn.com/problems/shu-zu-zhong-de-ni-xu-dui-lcof/
归并排序求逆序对
这题还有个树状数组的解法, 但是面试肯定不会问, 就不写了
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 class Solution {public : int ans = 0 ; vector <int > aux; void count (vector <int >& nums, int l, int mid, int r) { int p1 = l, p2 = mid + 1 ; int k = p1; while (p1 <= mid && p2 <= r) { if (nums[p1] <= nums[p2]) { aux[k++] = nums[p1++]; } else { aux[k++] = nums[p2++]; ans += (mid - p1 + 1 ); } } while (p1 <= mid) aux[k++] = nums[p1++]; while (p2 <= r) aux[k++] = nums[p2++]; for (int i = l; i <= r; ++i) nums[i] = aux[i]; } void merge_count (vector <int >& nums, int l, int r) { if (l >= r) return ; int mid = l + r >> 1 ; merge_count(nums, l, mid); merge_count(nums, mid + 1 , r); count(nums, l, mid, r); } int reversePairs (vector <int >& nums) { int l = 0 , r = nums.size() - 1 ; aux.resize(nums.size()); merge_count(nums, l, r); return ans; } };
89 | 单词搜索 https://leetcode-cn.com/problems/word-search/
爆搜回溯
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 class Solution {public : vector <int > dir = {-1 , 0 , 1 , 0 , -1 }; vector <vector <int >> vis; bool dfs (int x, int y, int k, string s, vector <vector <char >>& board) { if (board[x][y] != s[k]) return false ; if (k == s.size() - 1 ) return true ; vis[x][y] = true ; bool f = false ; for (int i = 0 ; i < 4 ; ++i) { int nx = x + dir[i], ny = y + dir[i + 1 ]; if (nx < 0 || nx >= board.size() || ny < 0 || ny >= board[0 ].size() || vis[nx][ny]) continue ; f = dfs(nx, ny, k + 1 , s, board); if (f) return true ; } vis[x][y] = false ; return f; } bool exist (vector <vector <char >>& board, string word) { int n = board.size(), m = board[0 ].size(); vis.resize(n, vector <int > (m, 0 )); for (int i = 0 ; i < n; ++i) { for (int j = 0 ; j < m; ++j) { if (board[i][j] == word[0 ]) { vis.assign(n, vector <int > (m, 0 )); if (dfs(i, j, 0 , word, board)) return true ; } } } return false ; } };
90 | 长度最小的子数组 https://leetcode-cn.com/problems/minimum-size-subarray-sum/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Solution {public : int minSubArrayLen (int target, vector <int >& nums) { int sum = 0 ; int ans = INT_MAX; for (int r = 0 , l = 0 ; r < nums.size(); ++r) { sum += nums[r]; while (sum >= target) { ans = min(ans, r - l + 1 ); sum -= nums[l++]; } } return ans == INT_MAX ? 0 : ans; } };
91 | 整数反转 https://leetcode-cn.com/problems/reverse-integer/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Solution {public : int reverse (int x) { int ans = 0 ; while (x) { if (ans < INT_MIN / 10 || ans > INT_MAX / 10 ) { return 0 ; } ans = ans * 10 + x % 10 , x /= 10 ; } return ans; } };
92 | 螺旋矩阵2 https://leetcode-cn.com/problems/spiral-matrix-ii/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Solution {public : vector <vector <int >> generateMatrix(int n) { int top = 0 , bottom = n - 1 , left = 0 , right = n - 1 , now = 1 ; vector <vector <int >> g (n, vector <int >(n, 0 )); while (1 ) { for (int i = left; i <= right; ++i) g[top][i] = now++; if (++top > bottom) return g; for (int i = top; i <= bottom; ++i) g[i][right] = now++; if (--right < left) return g; for (int i = right; i >= left; --i) g[bottom][i] = now++; if (--bottom < top) return g; for (int i = bottom; i >= top; --i) g[i][left] = now++; if (++left > right) return g; } return g; } };
93 | 最长有效括号 https://leetcode-cn.com/problems/longest-valid-parentheses/
栈得做法, 很巧妙
栈中(底部)保存得是「最后一个失配的右括号的下标」, 栈其他保存的是各个未匹配的左括号的下标
一旦产生括号匹配就先将匹配的左括号弹出, 再更新ans
边界条件是stk.push(-1)
, 先将栈中压入-1
, 保证每个右括号到来时都可以更新ans
当栈为空时, 表示出现了))
, 这时候更新栈底失配右括号的位置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Solution {public : int longestValidParentheses (string s) { stack <int > stk; stk.push(-1 ); int ans = 0 ; for (int i = 0 ; i < s.size(); ++i) { if (s[i] == '(' ) { stk.push(i); } else if (s[i] == ')' ) { stk.pop(); if (!stk.empty()) { ans = max(ans, i - stk.top()); } else { stk.push(i); } } } return ans; } };
94 | 搜索二维矩阵2 https://leetcode-cn.com/problems/search-a-2d-matrix-ii/
将矩阵以右上角为根看成一颗二叉搜索树
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Solution {public : bool searchMatrix (vector <vector <int >>& matrix, int target) { int n = matrix.size(), m = matrix[0 ].size(); int x = 0 , y = m - 1 ; while (x >= 0 && x < n && y >= 0 && y < m) { if (matrix[x][y] == target) return true ; if (matrix[x][y] > target) { --y; } else { ++x; } } return false ; } };
95 | 最大正方形 https://leetcode-cn.com/problems/maximal-square/
动态规划:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Solution {public : int dp[310 ][310 ]; int maximalSquare (vector <vector <char >>& mat) { memset (dp, 0 , sizeof dp); int n = mat.size(), m = mat[0 ].size(); int ans = 0 ; for (int i = 1 ; i <= n; ++i) { for (int j = 1 ; j <= m; ++j) { if (mat[i - 1 ][j - 1 ] == '1' ) { dp[i][j] = min(dp[i - 1 ][j], min(dp[i][j - 1 ], dp[i - 1 ][j - 1 ])) + 1 ; ans = max(ans, dp[i][j] * dp[i][j]); } } } return ans; } };
96 | 最长连续序列 https://leetcode-cn.com/problems/longest-consecutive-sequence/
记录就是桶排后中心扩展, 只是由于数据量大, 不能直接用数组模拟, 需要用哈希表
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 class Solution {public : int longestConsecutive (vector <int >& nums) { int ans = 0 ; unordered_set <int > hmap; for (auto i : nums) { hmap.insert(i); } while (hmap.size()) { int now = *hmap.begin(); hmap.erase(now); int next = now + 1 , prev = now - 1 ; while (hmap.count(next)) { hmap.erase(next); next++; } while (hmap.count(prev)) { hmap.erase(prev); prev--; } ans = max(ans, next - prev - 1 ); } return ans; } };
97 | 验证IP地址 https://leetcode-cn.com/problems/validate-ip-address/
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 class Solution {public : vector <string > split (string s, string spliter) { vector <string > ans; int it; int len =spliter.size(); while ((it = s.find(spliter)) && it != s.npos) { ans.push_back(s.substr(0 , it)); s = s.substr(it + len); } ans.push_back(s); return ans; } bool check_ipv4 (string s) { vector <string > ss = split(s, "." ); if (ss.size() != 4 ) return false ; for (string & str : ss) { if (str.empty() || str.size() > 3 || str.size() != 1 && str[0 ] == '0' ) return false ; for (auto i : str) if (!isdigit (i)) return false ; if (stoi(str) < 0 || stoi(str) > 255 ) return false ; } return true ; } bool check_ipv6 (string s) { vector <string > ss = split(s, ":" ); if (ss.size() != 8 ) return false ; for (string & str : ss) { if (str.empty()) return false ; for (auto i : str) { if (! (isdigit (i) || (i >= 'a' && i <= 'f' ) || (i >= 'A' && i <= 'F' ))) return false ; } if (str.size() == 0 || str.size() > 4 ) return false ; } return true ; } string validIPAddress (string IP) { return check_ipv4(IP) ? "IPv4" : check_ipv6(IP) ? "IPv6" : "Neither" ; } };
98 | 二叉树的完全性检验 二叉树(从1开始标记) 左边 2 x, 右边2 x + 1
利用这个性质判断遍历顺序即可
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 class Solution {public : bool isCompleteTree (TreeNode* root) { if (!root) return true ; queue <pair <TreeNode*, int >> q; q.emplace(root, 1 ); int cnt = 1 ; while (q.size()) { int sz = q.size(); for (int i = 0 ; i < sz; ++i) { auto [now, idx] = q.front(); q.pop(); if (idx != cnt++) return false ; if (now->left) q.emplace(now->left, idx * 2 ); if (now->right) q.emplace(now->right, idx * 2 + 1 ); } } return true ; } };
99 | 二叉搜索树与完全链表 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 class Solution {public : Node* prev = nullptr , *head = nullptr ; void dfs (Node* root) { if (!root) return ; dfs(root->left); if (prev) prev->right = root; else head = root; root->left = prev; prev = root; dfs(root->right); } Node* treeToDoublyList (Node* root) { if (!root) return root; dfs(root); prev->right = head, head->left = prev; return head; } };
100 | 不同路径 1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Solution {public : int dp[110 ]; int uniquePaths (int m, int n) { dp[0 ] = 1 ; for (int i = 0 ; i < m; ++i) { for (int j = 0 ; j < n; ++j) { if (j) dp[j] += dp[j - 1 ]; } } return dp[n - 1 ]; } };