《LeetcodeHot100非困难题补录》
最近比較閑,也比較焦慮,刷刷題吧
目錄
- 11. 盛最多水的容器
- 22. 括號生成
- 31. 下一個排列
- 48. 旋轉圖像
- 49. 字母異位詞分組
- 56. 合并區間
- 75. 顏色分類
- 79. 單詞搜索
- 114. 二叉樹展開為鏈表
- 141. 環形鏈表
- 148. 排序鏈表
- 152. 乘積最大子數組
- 169. 多數元素
- 207. 課程表(有向圖排序問題)
- 215. 數組中的第K個最大元素
- 208. 實現 Trie (前綴樹)
- 221. 最大正方形
- 234. 回文鏈表
- 238. 除自身以外數組的乘積
- 240. 搜索二維矩陣 II
- 283. 移動零
- 394. 字符串解碼
- 399. 除法求值
- 406. 根據身高重建隊列
- 437. 路徑總和 III
- 448. 找到所有數組中消失的數字
- 543. 二叉樹的直徑
- 560. 和為K的子數組
11. 盛最多水的容器
這個解釋挺好的
O(n) 雙指針解法:理解正確性、圖解原理(C++/Java)
兩次遍歷。
class Solution { public:int getlen(ListNode* head){int res = 0;ListNode* cur = head;while(cur != nullptr){cur = cur->next;res++;}return res;}ListNode* removeNthFromEnd(ListNode* head, int n) {ListNode* dummy_head = new ListNode(0);dummy_head->next = head;ListNode* cur = dummy_head;int len = getlen(head);int N = len - n;for(int i = 0; i < N; i++){cur = cur->next;}ListNode* tmp = cur->next;cur->next = cur->next->next;delete(tmp);ListNode* ret = dummy_head->next;delete dummy_head;return ret;} };前后指針法。
class Solution { public:ListNode* removeNthFromEnd(ListNode* head, int n) {ListNode* dummy_head = new ListNode(0,head);ListNode* first = dummy_head;ListNode* second = head;//second先行n個節點for(int i = 1; i <= n ;i++){second = second->next;}//兩個節點同時移動while(second){second = second->next;first = first->next;}//此時second處于鏈表尾部后面的空節點,first處于待刪除節點的前驅ListNode* tmp = first->next;first->next = first->next->next;delete(tmp);ListNode* ret = dummy_head->next;delete dummy_head;return ret;} };22. 括號生成
產生左分支時候,只看當前是否還有左括號可以使用。
產生右分支時候,還要受到左分支的限制。右邊剩余可以使用的括號數量一定得嚴格大于左邊剩余的數量時,才可以產生分支
典型回溯。
31. 下一個排列
不熟悉,還得多理解理解。
class Solution { public:void nextPermutation(vector<int>& nums) {int n = nums.size();if(n <= 1) return;int i = n-2;while(i >= 0 && nums[i] >= nums[i+1]){i--;}if(i < 0){reverse(nums.begin(),nums.end());return;}int j = n-1;while(j >= 0 && nums[i] >= nums[j]) j--;swap(nums[i],nums[j]);reverse(nums.begin()+i+1,nums.end());} };48. 旋轉圖像
草圖需要畫4x4的,不然具體swap的時候會搞亂掉。。。
class Solution { public:void rotate(vector<vector<int>>& matrix) {int pos1 = 0;int pos2 = matrix.size()-1;while(pos1 < pos2){int add = 0;while(add < pos2 - pos1){int tmp = matrix[pos1][pos1+add];matrix[pos1][pos1+add] = matrix[pos2-add][pos1];matrix[pos2-add][pos1] = matrix[pos2][pos2-add];matrix[pos2][pos2-add] = matrix[pos1+add][pos2];matrix[pos1+add][pos2] = tmp;add++;}pos1++;pos2--;}} };49. 字母異位詞分組
關鍵是hash表的結構。key是排序后的string,value是str數組,存放同一個key的元素。
class Solution { public:vector<vector<string>> groupAnagrams(vector<string>& strs) {unordered_map<string,vector<string>> umap;for(auto str : strs){string key = str;sort(key.begin(),key.end());umap[key].emplace_back(str);}vector<vector<string>> result;for(auto it = umap.begin(); it != umap.end(); it++){result.emplace_back(it->second);}return result;} };56. 合并區間
感覺下面的這個自己的寫法還是有點繁瑣。。。
可以看我的每一步融合后對原數組的影響。
這邊貼一下甜姨的解法,以及她整理的相關題目,留著以后再刷:
吃🐳!🤷?♀?竟然一眼秒懂合并區間!
75. 顏色分類
class Solution { public:void sortColors(vector<int>& nums) {int size = nums.size();if(size < 2) return;int zero = 0;int two = size;int i = 0;while(i < two){if(nums[i] == 0){swap(nums[zero],nums[i]);zero++;i++;}else if(nums[i] == 1){i++;}else{two--;swap(nums[i],nums[two]);}}return;} };79. 單詞搜索
回溯法,類似的題有120、200
class Solution { public:int direction[5] = {-1,0,1,0,-1};bool dfs(vector<vector<char>>& board,vector<vector<bool>>& visited,string word,int index,int i,int j){if(index == word.size()-1) return board[i][j] == word[index];if(board[i][j] == word[index]){visited[i][j] = true;for(int k = 0; k < 4; k++){int x = i + direction[k];int y = j + direction[k+1];if(x >= 0 && x < board.size() && y >=0 && y < board[0].size() && visited[x][y] == false){if(dfs(board,visited,word,index+1,x,y) == true) return true;}}visited[i][j] = false;}return false;}bool exist(vector<vector<char>>& board, string word) {int m = board.size();int n = board[0].size();vector<vector<bool>> visited(m,vector<bool>(n,false));for(int i = 0; i < m; i++){for(int j = 0; j < n; j++){if(dfs(board,visited,word,0,i,j) == true) return true;}}return false;} };114. 二叉樹展開為鏈表
class Solution { public:vector<TreeNode*> num;void preorder(TreeNode* root){num.emplace_back(root);if(root->left) preorder(root->left);if(root->right) preorder(root->right);}void flatten(TreeNode* root) {num.clear();if(root == nullptr) return;preorder(root);for(int i = 1; i < num.size(); i++){TreeNode* prev = num[i-1];TreeNode* cur = num[i];prev->right = cur;prev->left = nullptr;}} };141. 環形鏈表
快慢指針法
class Solution { public:bool hasCycle(ListNode *head) {ListNode* slow = head;ListNode* fast = head;while(slow && fast && fast->next){slow = slow->next;fast = fast->next->next;if(slow == fast) return true;}return false;} };148. 排序鏈表
getmid和merge子函數要好好寫。關于遞歸調用的順序也要注意。
這個圖很好,可以很好說明思路
152. 乘積最大子數組
class Solution { public:int maxProduct(vector<int>& nums) {int n = nums.size();vector<int> maxn(n,INT_MIN);vector<int> minn(n,INT_MAX);maxn[0] = minn[0] = nums[0];int res = nums[0];for(int i = 1; i < n; i++){if(nums[i] > 0){maxn[i] = max(nums[i],maxn[i-1] * nums[i]);minn[i] = min(nums[i],minn[i-1] * nums[i]);}else{maxn[i] = max(nums[i],minn[i-1] * nums[i]);minn[i] = min(nums[i],maxn[i-1] * nums[i]);}res = max(res,maxn[i]);}return res; } };169. 多數元素
這一題的投票法比較妙。
投票法,眾數個數至少比非眾數多一,把COUNT加減當作一個眾數抵消掉一個非眾數,剩下的一定是眾數。
207. 課程表(有向圖排序問題)
將一個有向無環圖轉成線性的排序就叫拓撲排序。
每次只能選入度為0的課,因為它不依賴于其他課,是當下能上的課。
BFS:
1、讓入度為0的課入列,它們是能直接選的課
2、然后逐個出列,出列代表著課被選,需要減少相關課的入度
3、如果相關課的入度變為0,安排它入列
每門課的入度需要被記錄,我們關心入度值得變化。
課程之間得依賴關系也要被記錄,我們關心選當前課會減少哪些課得入度
所以我們準備入度數組:
課號0->n-1作為索引,通過遍歷先決條件求出對應得初始入度
鄰接表:用哈希表記錄依賴關系
key:課號
value:依賴于這門課得后續課,放入一個數組中
判斷能否修完所有課:
用一個變量count記錄入列得頂點個數,最后判斷count是否等于總課程數
215. 數組中的第K個最大元素
topk問題,姊妹問題為牛客網的最小的k個數。
求最大K,用降序排列的優先隊列priority_queue<int,vector<int>,greater<int>> que;
求最小K,用升序排列的優先隊列priority_queue<int> que;
208. 實現 Trie (前綴樹)
這篇講解很到位
Trie Tree 的實現 (適合初學者)
221. 最大正方形
dp法:
使用動態規劃降低時間復雜度,用dp(i,j)表示以(i,j)為右下角,且只包含1地正方形地邊長最大值。
如果能計算出所有dp(i,j)的值,那么其中的最大值即為矩陣中只包含1的正方形的邊長最大值。
其平方和為最大正方形的面積。
if matrix[i][j] == 0 => dp(i,j) = 0
if matrix[i][j] == 1 => dp(i,j) = min(dp(i-1,j),dp(i-1,j-1),dp(i,j-1)+1
邊界條件:
i == 0 || j == 0,則dp(i,j) = 1
234. 回文鏈表
思路:
1、先使用快慢指針找到鏈表的中間節點,也就是將鏈表分為前后兩個鏈表
2、將后面的鏈表進行反轉
3、比較兩個鏈表是否相同
/*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode() : val(0), next(nullptr) {}* ListNode(int x) : val(x), next(nullptr) {}* ListNode(int x, ListNode *next) : val(x), next(next) {}* };*/ class Solution { public:ListNode* getMidNode(ListNode* head){if(head == nullptr || head->next == nullptr)return head;ListNode* slow = head;ListNode* fast = head->next->next;while(fast && fast->next){slow = slow->next;fast = fast->next->next;}return slow;}ListNode* reverseList(ListNode* head){if(head == nullptr || head->next == nullptr) return head;ListNode* prev = nullptr;ListNode* cur = head;while(cur){ListNode* tmp = cur->next;cur->next = prev;prev = cur;cur = tmp;}return prev;}bool isPalindrome(ListNode* head) {ListNode* first = head;ListNode* bef_second = getMidNode(head)->next;ListNode* second = reverseList(bef_second);while(first && second){if(first->val != second->val) return false;first = first->next;second = second->next;}return true;} };238. 除自身以外數組的乘積
只開辟一個右邊乘積數組,另外左邊乘積在for循環種計算。
class Solution { public:vector<int> productExceptSelf(vector<int>& nums) {int n = nums.size();;vector<int> L(n,1);for(int i = nums.size()-1; i >= 0; i--){if(i == nums.size()-1) L[i] = 1;else L[i] = L[i+1]*nums[i+1]; //cout << L[i] << endl;}int R = 1;vector<int> result(n);for(int i = 0; i < nums.size(); i++){result[i] = R * L[i];R *= nums[i]; }return result;} };240. 搜索二維矩陣 II
從左下角往右上搜索。
若當前矩陣的元素值 == target,則直接返回 true。
若當前矩陣的元素值 > target,則向上移動一行列不變,即 matrix[i][j] 變為 matrix[i - 1][j],繼續進行比較。
若當前矩陣的元素值 < target,則向右移動一列行不變,即 matrix[i][j] 變為 matrix[i][j + 1],繼續進行比較。
class Solution { public:bool searchMatrix(vector<vector<int>>& matrix, int target) {int n = matrix.size();int m = matrix[0].size();int hang = n - 1;int lie = 0;while(lie < m && hang >= 0){if(target == matrix[hang][lie]) return true;else if(target > matrix[hang][lie]) lie++;else hang--;}return false;} };283. 移動零
與75顏色分類類似。不太熟悉。
使用雙指針法,左指針指向當前已經處理好的序列的尾部,右指針指向待處理序列的頭部。
右指針不斷向右移動,每次右指針指向非零數,則將左右指針對應的數交換,同時左指針右移。
左指針左邊均為非0數
右指針左邊知道左指針處均為0,所以每次交換都是將左值幀的0與右指針的非零交換。
class Solution { public:void moveZeroes(vector<int>& nums) {if(nums.size() <= 1) return;int left = 0;for(int right = 0; right < nums.size(); right++){if(nums[right] != 0){swap(nums[left],nums[right]);left++;}}} };394. 字符串解碼
把字母、數字、括號看成獨立的TOKEN,并用棧來維護這些TOKEN。
遍歷數組:
1、如果當前的字符為數位,解析出一個數字(連續的多個數位)并進棧
2、如果當前的字符為字母或者左括號,直接進棧
3、如果當前字符為右括號,開始出棧,一直到左括號出棧,出棧序列反轉后拼接成一個字符串,此時取出棧頂的數字,就是這個字符串應該出現的次數,根據次數和字符串構造出新的字符串并進棧。
將棧中的元素按照從棧底到棧頂的順序拼接起來,就得到了答案。
399. 除法求值
考察圖的深搜回溯以及一些復雜數據結構的構建。
class Solution { public:vector<double> res;bool ifFind;void dfs(unordered_map<string,vector<pair<string,double>>>& umap,unordered_map<string,int>& visited, string& now, string& target,double path){if(ifFind == false) return;if(now == target){ifFind = false;res.emplace_back(path);return;}//對和該節點相連的每個節點進行深搜for(int i = 0; i < umap[now].size(); i++){if(visited[umap[now][i].first] == 0){visited[umap[now][i].first] = 1;dfs(umap,visited,umap[now][i].first,target,path*umap[now][i].second);visited[umap[now][i].first] = 0;}}return;}vector<double> calcEquation(vector<vector<string>>& equations, vector<double>& values, vector<vector<string>>& queries) {unordered_map<string,vector<pair<string,double>> > umap;unordered_map<string,int> visited;//將每個節點與相連節點的邊的權值計算出來for(int i = 0; i < equations.size(); i++){umap[equations[i][0]].emplace_back( make_pair(equations[i][1],values[i]) );umap[equations[i][1]].emplace_back( make_pair(equations[i][0],1.0/values[i]) );}//對每個節點進行深搜for(int i = 0; i < queries.size(); i++){if(umap.find(queries[i][0]) == umap.end()){res.emplace_back(-1.0);}else{ifFind = true;dfs(umap,visited,queries[i][0],queries[i][1],1);if(ifFind) res.emplace_back(-1.0);}}return res;} };406. 根據身高重建隊列
class Solution { public:vector<vector<int>> reconstructQueue(vector<vector<int>>& nums) {int n = nums.size();vector<vector<int>> ans;sort(nums.begin(),nums.end(),[](const vector<int>& a,const vector<int>& b){//[0]降序,若相等,[1]升序if(a[0] != b[0])return a[0] > b[0];return a[1] < b[1];}); for(int i = 0; i < nums.size(); i++){//結果集中元素個數大于第i個人前面應該有的人數時,將第i個人插入到結果集的[i][1]位置if(ans.size() > nums[i][1])ans.insert(ans.begin()+nums[i][1],nums[i]);//結果集中元素個數等于第i個人前面應有的人數時,將第i個人追加到結果集后面。elseans.emplace_back(nums[i]);}return ans;} };437. 路徑總和 III
巧妙使用前綴和,思路主要看注釋,就是回溯+前綴和Map。與下面的560有相似的思想
/*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), right(nullptr) {}* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}* };*/ class Solution { public:int target;unordered_map<int,int> curSumMap; //保存前綴樹,key為根節點到此節點的前綴和,value為該前綴和出現的次數int dfs(TreeNode* node,int curSum){if(node == nullptr) return 0;int ans = 0;curSum += node->val; //得到當前前綴樹的值//兩節點間的路徑和 = 兩節點的前綴和之差,所以當前節點的路徑和減去目標值的路徑和就等于其某個祖先節點的路徑和,觀察有幾個這樣的祖先節點。ans += curSumMap[curSum - target]; //得到我們想要前綴樹的個數,想要前綴樹值就是當前前綴樹值減去目標值curSumMap[curSum] += 1; //將當前前綴樹的值保存,前綴和為curSum的節點個數+1//搜索左右子節點ans += dfs(node->left,curSum);ans += dfs(node->right,curSum);curSumMap[curSum] -= 1; //防止左邊前綴樹影響右邊前綴樹,左邊前綴樹可能有個為6,右邊正好想要一個前綴樹為6的,這樣子就出錯了return ans; //結果是當前節點前綴樹的個數加上左邊滿足的個數加右邊滿足的個數}int pathSum(TreeNode* root, int targetSum) {target = targetSum;curSumMap[0] = 1; //前綴樹為0的個數至少是一個return dfs(root,0);} };448. 找到所有數組中消失的數字
原地哈希。不算簡單。
class Solution { public:vector<int> findDisappearedNumbers(vector<int>& nums) {for(int i = 0; i < nums.size(); i++){int x = (nums[i]-1) % nums.size();nums[x] += nums.size(); }vector<int> res;for(int i = 0; i < nums.size(); i++){if(nums[i] <= nums.size()) res.emplace_back(i+1);}return res;} };543. 二叉樹的直徑
先求每個節點的左右子節點的最大深度,然后加起來的值和maxlen比較更新。然后更新當前節點的最大深度。
class Solution { public:int maxlen = 0;int getDepth(TreeNode* node){if(node == NULL) return 0;int left=getDepth(node->left);int right=getDepth(node->right);maxlen = max(maxlen,left+right);return max(left,right)+1;}int diameterOfBinaryTree(TreeNode* root) {if(!root->left && !root->right) return 0;getDepth(root);return maxlen;} };560. 和為K的子數組
求[j…i]子數組和為k,可以轉化為
pre[i] - pre[j-1] == k
移項后:
pre[j-1] == pre[i] - k
考慮以i結尾的和為k的連續子數組個數只要統計有多少個前綴和為pre[i] - k的pre[j]即可。
哈希表umap,以和為key,出現的次數為value。
從左往右邊更新umap邊計算答案。
總結
以上是生活随笔為你收集整理的《LeetcodeHot100非困难题补录》的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 信阳治疗子宫发育不良最好的医院推荐
- 下一篇: 《线程管理:线程基本操作》