解题均采用C++
哈希
两数之和
1 2 3 4 5 6 7 8 9 10 11 12 class Solution {public : vector<int > twoSum (vector<int >& nums, int target) { for (int i=0 ;i<nums.size ()-1 ;i++) for (int j=i+1 ;j<nums.size ();j++) { if (nums[i]+nums[j]==target) return {i,j}; } return {}; } };
复杂度O(n²),击败69.02%
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Solution {public : vector<int > twoSum (vector<int >& nums, int target) { unordered_map<int , int >hash_map; for (int i =0 ;i<nums.size ();i++) { int complentary=target-nums[i]; if (hash_map.find (complentary)!=hash_map.end ()) return {hash_map[complentary],i}; hash_map[nums[i]]=i; } return {}; } };
复杂度O(n),击败100%。
字母异位词分组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Solution {public : vector<vector<string>> groupAnagrams (vector<string>& strs) { unordered_map<string, vector<string>> hash_map; for (const string& s : strs) { string key = s; sort (key.begin (), key.end ()); hash_map[key].push_back (s); } vector<vector<string>> result; for (auto & pair : hash_map) { result.push_back (pair.second); } return result; } };
15ms 击败78.46%
复杂度O(n × k log k),半靠AI写的,主要是要想到以字符串为键,我原先单纯以int为键,string为value,判断has_map存在顺序版本再加进去,就需要创建多个hash_map版本,很丑陋
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<string>> groupAnagrams (vector<string>& strs) { int primes[] = {2 , 3 , 5 , 7 , 11 , 13 , 17 , 19 , 23 , 29 , 31 , 37 , 41 , 43 , 47 , 53 , 59 , 61 , 67 , 71 , 73 , 79 , 83 , 89 , 97 , 101 }; unordered_map<unsigned long long , vector<string>> hash_map; for (const string& s : strs) { unsigned long long key = 1 ; const long long MOD = 1e9 + 7 ; for (char c : s) { key = (key*primes[c - 'a' ])%MOD; } hash_map[key].push_back (s); } vector<vector<string>> result; for (auto & pair : hash_map) { result.push_back (pair.second); } return result; } };
12ms 击败85.23%
23.59MB 击败91.32%
不再直接用字符串作键,无论速度还是内存都更好,只是MOD需要足够大,不然key会重复,算是逃课做法,正经应该有处理冲突
最长连续序列
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 longestConsecutive (vector<int >& nums) { unordered_set<int > hash_set (nums.begin(),nums.end()) ; int longest=0 ; for (int num:hash_set) { if (!hash_set.count (num-1 )) { int current_num=num; int current_length=1 ; while (hash_set.count (current_num+1 )) { current_num+=1 ; current_length+=1 ; } longest=max (current_length,longest); } } return longest; } };
91ms 击败83.17%
学了一种新的哈希表unordered_set,他的count()适合判断某个值是否存在过
另外一个有趣的是直接遍历nums会超时,但如果遍历整理过的hash_set不会超时
双指针
移动零
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Solution {public : void moveZeroes (vector<int >& nums) { int fast=0 ,slow=0 ; for (fast=0 ;fast<nums.size ();fast++) { if (nums[fast]!=0 ) { int temp=nums[fast]; nums[fast]=0 ; nums[slow++]=temp; } } } };
复杂度O(n)
0ms 击败100.00%
维护双指针即可
盛最多的水
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Solution {public : int maxArea (vector<int >& height) { int fast=0 ,slow=0 ; int maxArea=0 ; for (slow=0 ;slow<height.size ();slow++) for (fast=height.size ()-1 ;fast>slow;fast--) { int length=min (height[slow],height[fast]); int wide = fast-slow; maxArea=max (maxArea,length*wide); } return maxArea; } };
O(n^2)超时
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Solution {public : int maxArea (vector<int >& height) { int left=0 ,right=height.size ()-1 ; int maxArea=0 ; while (right>left) { int length=min (height[left],height[right]); int wide = right-left; maxArea=max (maxArea,length*wide); if (height[left] < height[right]) left++; else right--; } return maxArea; } };
复杂度O(n)
4ms击败25.93%
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 maxArea (vector<int >& height) { int left=0 ,right=height.size ()-1 ; int maxArea=0 ; while (right>left) { int length=min (height[left],height[right]); int wide = right-left; maxArea=max (maxArea,length*wide); if (height[left] < height[right]) { int last_h = height[left]; while (left < right && height[left] <= last_h) left++; } else { int last_h = height[right]; while (left < right && height[right] <= last_h) right--; } } return maxArea; } };
相当于一个小剪枝吧
0ms 击败100.00%
三数之和
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<vector<int >> threeSum (vector<int >& nums) { if (nums.size () < 3 ) return {}; sort (nums.begin (), nums.end ()); int i=0 ,j=0 ; int length=nums.size (); vector<vector<int >> res; for (i=0 ;i<length;i++) { if (nums[i] > 0 ) break ; while (i>0 && i<length && nums[i]==nums[i-1 ]) i++; unordered_set<int >seen; for (j=i+1 ;j<length;j++) { int complentary=-nums[j]-nums[i]; if (seen.count (complentary)) { res.push_back ({nums[i],nums[j],complentary}); while (j+1 <length && nums[j]==nums[j+1 ]) j++; } seen.insert (nums[j]); } } return res; } };
复杂度O(n^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 36 37 38 class Solution {public : vector<vector<int >> threeSum (vector<int >& nums) { if (nums.size () < 3 ) return {}; sort (nums.begin (), nums.end ()); int length=nums.size (); vector<vector<int >>res; for (int i=0 ;i<length;i++) { if (nums[i]>0 )break ; while (i>0 && i<length && nums[i]==nums[i-1 ]) i++; int l=i+1 ,r=length-1 ; while (r>l) { int num = nums[i]+nums[r]+nums[l]; if (num==0 ) { res.push_back ({nums[i],nums[r],nums[l]}); while (l+1 <r && nums[l]==nums[l+1 ]) l++; while (r-1 >l && nums[r]==nums[r-1 ]) r--; l++; r--; } else if (num>0 ) { while (r-1 >l && nums[r]==nums[r-1 ]) r--; r--; } else { while (l+1 <r && nums[l]==nums[l+1 ]) l++; l++; } } } return res; } };
63ms 击败31.78%
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 : vector<vector<int >> threeSum (vector<int >& nums) { if (nums.size () < 3 ) return {}; sort (nums.begin (), nums.end ()); int length=nums.size (); vector<vector<int >>res; for (int i=0 ;i<length-2 ;i++) { if (nums[i]>0 )break ; if (i > 0 && nums[i] == nums[i - 1 ]) continue ; if ((long )nums[i] + nums[i + 1 ] + nums[i + 2 ] > 0 ) break ; if ((long )nums[i] + nums[length - 2 ] + nums[length - 1 ] < 0 ) continue ; int l=i+1 ,r=length-1 ; int target = -nums[i]; while (r>l) { int num = nums[l] + nums[r]; if (num==target) { res.push_back ({nums[i],nums[r],nums[l]}); while (l+1 <r && nums[l]==nums[l+1 ]) l++; l++; while (r-1 >l && nums[r]==nums[r-1 ]) r--; r--; } else if (num>target) { r--; } else { l++; } } } return res; } };
40ms 击败96.08%
接雨水
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 trap (vector<int >& height) { int n = height.size (); if (n < 3 ) return 0 ; int total = 0 ; int peak = 0 ; int slow = 0 ; for (int fast = 1 ; fast < n; ++fast) { if (height[fast]==0 )continue ; if (height[fast] >= height[slow]) { for (int i = slow + 1 ; i < fast; ++i) { total += (height[slow] - height[i]); } slow = fast; } } peak = slow; slow = n-1 ; for (int fast = n-2 ;fast>=peak;fast--) { if (height[fast]==0 )continue ; if (height[fast] >= height[slow]) { for (int i = slow - 1 ; i > fast; --i) { total += (height[slow] - height[i]); } slow = fast; } } return total; } };
自己胡乱搞的方法,竟然效果不错,时间复杂度O(n),空间复杂度O(1)
0ms 击败100.00%
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 : int trap (vector<int >& height) { int n = height.size (); if (n < 3 ) return 0 ; int left = 0 ,right=n-1 ; int total = 0 ; int leftMax=0 ,rightMax=0 ; while (left<right) { if (height[left]<height[right]) { if (height[left]>=leftMax) leftMax=height[left]; else total+=leftMax-height[left]; left++; } else { if (height[right]>=rightMax) rightMax=height[right]; else total+=rightMax-height[right]; right--; } } return total; } };
也是时间复杂度O(n),空间复杂度O(1),0ms 击败100.00%
但我觉得应该是这种方法更快
滑动窗口
无重复字符的最长子串
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Solution {public : int lengthOfLongestSubstring (string s) { int n = s.size (); vector<int > last_pos (128 , -1 ) ; int left=-1 ; int MaxLength=0 ; for (int right = 0 ; right < s.size (); ++right) { int current_left = last_pos[s[right]]; if (current_left>left) left = current_left; last_pos[s[right]]=right; MaxLength = max (MaxLength,AIright-left); } return MaxLength; } };
AI给的解法思路,确实相当精妙
复杂度O(n)
0ms 击败100.00%
字母异位词
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 > findAnagrams (string s, string p) { int n = s.size (); int m = p.size (); if (n<m) return {}; vector<int > char_pcnt (26 , 0 ) ; vector<int > char_ncnt (26 , 0 ) ; vector<int > res; for (int i=0 ;i<m;i++) { char_pcnt[p[i]-'a' ]++; char_ncnt[s[i]-'a' ]++; } int i=0 ; while (i+m-1 <n) { if (char_ncnt==char_pcnt) res.push_back (i); char_ncnt[s[i]-'a' ]--; if (i+m<n) char_ncnt[s[i+m]-'a' ]++; i++; } return res; } };
复杂度O(n),只是char_ncnt==char_pcnt这样内含26次比较,可进一步优化
0ms 击败100.00%
子串
和为k的子数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Solution {public : int subarraySum (vector<int >& nums, int k) { int n = nums.size (); int cnt=0 ; for (int i = 0 ;i<n;i++) { int result = 0 ; for (int j = i;j<n;j++) { result+=nums[j]; if (result==k) { cnt++; } } } return cnt; } };
复杂度O(n^2),2904ms 击败5.01%
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Solution {public : int subarraySum (vector<int >& nums, int k) { int n = nums.size (); int *prefix = (int *)malloc ((n+1 ) * sizeof (int )); int cnt=0 ; for (int i=1 ;i<=n;i++) prefix[i]=prefix[i-1 ]+nums[i-1 ]; for (int i = 0 ;i<n;i++) { for (int j = i;j<n;j++) { if (prefix[j+1 ]-prefix[i]==k) cnt++; } } return cnt; } };
还是O(n^2),但节省了每次计算result
2116ms 击败12.89%
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Solution {public : int subarraySum (vector<int >& nums, int k) { int n = nums.size (); int *prefix = (int *)calloc (n+1 , sizeof (int )); unordered_map<int , int > hash_map; int cnt=0 ; for (int i=1 ;i<=n;i++) prefix[i]=prefix[i-1 ]+nums[i-1 ]; for (int i = 0 ;i<n;i++) { hash_map[prefix[i]]++; int target = prefix[i+1 ] - k; if (hash_map.count (target)) { cnt += hash_map[target]; } } return cnt; } };
复杂度O(n)
51ms 击败43.88%
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Solution {public : int subarraySum (vector<int >& nums, int k) { int n = nums.size (); unordered_map<int , int > hash_map; int cnt=0 ; int pre=0 ; for (int i = 0 ;i<n;i++) { hash_map[pre]++; pre+=nums[i]; int target = pre - k; if (hash_map.find (pre-k)!=hash_map.end ()) { cnt += hash_map[target]; } } return cnt; } };
前缀和的计算可以放在循环中来优化,另外发现hash_map.find()比hash_map.count()更快
复杂度O(n) 36ms 击败88.47%
滑动窗口最大值
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 : void max_update (int & now_max, int new_num, int old_num, vector<int >& nums, int start, int end) { if (new_num >= now_max) { now_max = new_num; } else if (old_num == now_max) { now_max = nums[start]; for (int i = start + 1 ; i <= end; i++) { if (nums[i] > now_max) now_max = nums[i]; } } } vector<int > maxSlidingWindow (vector<int >& nums, int k) { if (nums.empty ()) return {}; vector<int > res; int max_num = nums[0 ]; for (int i = 1 ; i < k; i++) { max_num = max (max_num, nums[i]); } res.push_back (max_num); for (int i = k; i < nums.size (); i++) { int old_num = nums[i - k]; int new_num = nums[i]; max_update (max_num, new_num, old_num, nums, i - k + 1 , i); res.push_back (max_num); } return res; } };
复杂度O(NK),超时
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 : void deque_update (deque<int >&dq,int old_num,int new_num) { if (dq.front ()==old_num) dq.pop_front (); while (!dq.empty ()&&dq.back ()<new_num) dq.pop_back (); dq.push_back (new_num); } vector<int > maxSlidingWindow (vector<int >& nums, int k) { if (nums.empty ()) return {}; vector<int > res; deque<int >dq; for (int i=0 ;i<k;i++) { while (!dq.empty () && dq.back () < nums[i]) dq.pop_back (); dq.push_back (nums[i]); } res.push_back (dq.front ()); for (int i=k;i<nums.size ();i++) { int old_idx=i-k; int new_idx=i; int old_num=nums[old_idx]; int new_num=nums[new_idx]; deque_update (dq,old_num,new_num); res.push_back (dq.front ()); } return res; } };
复杂度O(N),queue中只需存放大于new_num的数
23ms 击败82.29%
最小覆盖子串
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 : string minWindow (string s, string t) { int m = s.size (); int n = t.size (); if (m<n)return {}; int need[128 ] = {0 }; int window[128 ] = {0 }; pair<int , int > result = {0 , INT_MAX}; int left=0 ,right=0 ,valid=0 ,requiredTypes=0 ; for (char c : t) { if (need[c] == 0 ) requiredTypes++; need[c]++; } while (right < m) { char c = s[right]; right++; if (need[c] > 0 ) { window[c]++; if (window[c] == need[c]) { valid++; } } while (valid == requiredTypes) { if (right - left < result.second - result.first) { result = {left, right}; } char d = s[left++]; if (need[d] > 0 ) { if (window[d] == need[d]) valid--; window[d]--; } } } if (result.second==INT_MAX) return {}; int start = result.first; int len = result.second - result.first; string sub = s.substr (start, len); return sub; } };
复杂度是O ( m + n ) O(m + n) O ( m + n ) ,0ms 击败100.00%
最大数组和
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Solution {public : int maxSubArray (vector<int >& nums) { int n = nums.size (); int max_subsum=nums[0 ],min_sum=0 ; int current_pre_sum=0 ; for (int i = 0 ;i<n;i++) { current_pre_sum+=nums[i]; max_subsum=max (max_subsum,current_pre_sum-min_sum); min_sum=min (min_sum,current_pre_sum); } return max_subsum; } };
复杂度O(n),0ms 击败100.00%
也是比较简单的,只要那当前最大前缀和减去在这之前的最小前缀和即可
1 2 3 4 5 6 7 8 9 10 11 12 13 class Solution {public : int maxSubArray (vector<int >& nums) { int max_res=nums[0 ]; int current_dp=0 ; for (int x:nums) { current_dp=max (x,current_dp+x); max_res=max (max_res,current_dp); } return max_res; } };
复杂度O(n),0ms 击败100.00%
也是比较经典的了
合并区间
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) { vector<vector<int >>res; sort (intervals.begin (), intervals.end ()); res.push_back (intervals[0 ]); int n = intervals.size (); for (int i=1 ;i<n;i++) { vector<int >& last = res.back (); vector<int > now = intervals[i]; if (now[0 ]<=last[1 ]&&now[1 ]>=last[1 ]) last[1 ]=now[1 ]; else if (now[0 ]>last[1 ]) res.push_back (now); } return res; } };
复杂度O ( n log n ) O(n \log n) O ( n log n ) (都在sort上了)15ms 击败13.61%
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 >> merge (vector<vector<int >>& intervals) { if (intervals.empty ()) return {}; vector<vector<int >>res; sort (intervals.begin (), intervals.end ()); res.push_back (intervals[0 ]); int n = intervals.size (); for (int i=1 ;i<n;i++) { vector<int >& last = res.back (); vector<int > now = intervals[i]; if (now[0 ] <= last[1 ]) { last[1 ] = max (last[1 ], now[1 ]); } else res.push_back (now); } return res; } };
简单优化掉了一些判断逻辑 7ms击败60.63%
缺失的第一个正数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Solution {public : int firstMissingPositive (vector<int >& nums) { int n=nums.size (); for (int i=0 ;i<n;i++) { while (nums[i]>0 &&nums[i]-1 <n&& nums[nums[i]-1 ]!=nums[i]) swap (nums[i],nums[nums[i]-1 ]); } for (int i=0 ;i<n;i++) if (nums[i]!=i+1 ) return i+1 ; return n+1 ; } };
换凳子想法,复杂度O(n)(虽然有个while,但大部分很早能停止)
0ms 击败100.00%
前面写一个哈希表的,直接插入,到最后看哪个不在,更好想,但六十多ms
矩阵
矩阵置零
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 class Solution {public: void setZeroes(vector<vector<int >>& matrix) { int row = matrix.size(); int column = matrix[0 ].size(); bool column_0 =false; bool row_0=false; for (int j=0 ;j<column;j++) if (matrix[0 ][j]==0 ) row_0=true; for (int i=0 ;i<row;i++) if (matrix[i][0 ]==0 ) column_0=true; for (int j=1 ;j<column;j++) { for (int i=1 ;i<row;i++) { if (matrix[i][j]==0 ) { matrix[i][0 ]=0 ; matrix[0 ][j]=0 ; } } } for (int j=1 ;j<column;j++) if (matrix[0 ][j]==0 ) for (int i=1 ;i<row;i++) matrix[i][j]=0 ; for (int i=1 ;i<row;i++) if (matrix[i][0 ]==0 ) for (int j=1 ;j<column;j++) matrix[i][j]=0 ; if (column_0) for (int i=0 ;i<row;i++) matrix[i][0 ]=0 ; if (row_0) for (int j=0 ;j<column;j++) matrix[0 ][j]=0 ; } };
复杂度O(N×M)
0ms 击败100.00%
代码逻辑写的有些冗杂,但能跑就行
螺旋矩阵
写死的四边界就不写了,题解有一个很高效的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Solution {public : static constexpr int dire[4 ][2 ] = {{0 , 1 }, {1 , 0 }, {0 , -1 }, {-1 , 0 }}; vector<int > spiralOrder (vector<vector<int >>& matrix) { int m = matrix.size (), n = matrix[0 ].size (); int size = m * n; vector<int > ans; int i = 0 , j = -1 ; for (int di = 0 ; ans.size () < size; di = (di + 1 ) % 4 ) { for (int k = 0 ; k < n; ++k) { i += dire[di][0 ]; j += dire[di][1 ]; ans.push_back (matrix[i][j]); } swap (--m, n); } return ans; } };
通过交换m,n收缩避免了四边界的冗余逻辑
转置矩阵
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Solution {public: void rotate(vector<vector<int >>& matrix) { int n = matrix.size(); // 1. 转置矩阵(沿主对角线翻转) for (int i = 0 ; i < n; i++) { for (int j = i + 1 ; j < n; j++) { swap(matrix[i][j], matrix[j][i]); } } // 2. 每行翻转(左右翻转) for (int i = 0 ; i < n; i++) { reverse(matrix[i].begin(), matrix[i].end()); } } };
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: void rotate(vector<vector<int >>& matrix) { int size=matrix.size(); int start=0 ,end=size-1 ; while (end-start>0 ) { for (int i=start;i<end;i++) { int temp=start; int y=start,x=i; for (int j=0 ;j<3 ;j++) {swap(matrix[y][x],matrix[size-1 -x][y]); int y_temp=y; y=size-1 -x; x=y_temp; } } start++; end--; } } };
自己写的比较脏的
搜索二维矩阵
暴力遍历
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 bool searchMatrix (vector<vector<int >>& matrix, int target) { int m =matrix.size (); int n =matrix[0 ].size (); int y_idx=0 ,x_idx=0 ; while (y_idx<n&&matrix[0 ][y_idx++]<target); while (x_idx<m&&matrix[x_idx++][0 ]<target); for (int i=0 ;i<y_idx;i++) { if (matrix[0 ][i]>target) continue ; if (matrix[x_idx-1 ][i]<target) continue ; for (int j=0 ;j<x_idx;j++) if (matrix[j][i]==target) return true ; } return false ; }
自己写的比较脏的 63ms
右上角剪枝搜索
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 m =matrix.size (); int n =matrix[0 ].size (); int i=0 ,j=n-1 ; while (i<m&&j>=0 ) { if (matrix[i][j]>target) j--; else if (matrix[i][j]<target) i++; else return true ; } return false ; } };
复杂度O(m+n),43ms
图像旋转
1 2 3 4 5 6 7 8 9 10 11 12 class Solution {public : void rotate (vector<vector<int >>& matrix) { int size=matrix.size (); vector<vector<int >>r_matrix=matrix; for (int i=0 ;i<size;i++) for (int j=0 ;j<size;j++) { matrix[i][j]=r_matrix[size-1 -j][i]; } } };
粗暴遍历就能过
链表
相交链表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Solution {public : ListNode *getIntersectionNode (ListNode *headA, ListNode *headB) { if (headA == nullptr || headB == nullptr ) return nullptr ; ListNode* pA = headA; ListNode* pB = headB; while (pA != pB) { pA = (pA == nullptr ) ? headB : pA->next; pB = (pB == nullptr ) ? headA : pB->next; } return pA; } };
复杂度O(m+n)
反转链表
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 : ListNode* reverseList (ListNode* head) { if (head == nullptr || head->next == nullptr ) { return head; } ListNode* newHead=reverseList (head->next); head->next->next=head; head->next=nullptr ; return newHead; } };
关键是要想通
环形链表
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 hasCycle (ListNode *head) { while (head!=nullptr ) { if (head->val == INT_MAX) return true ; head->val=INT_MAX; head=head->next; } return false ; } };
逃课写法 复杂度O(n)
12ms 击败42.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 class Solution {public : bool hasCycle (ListNode *head) { if (head == nullptr || head->next == nullptr ) { return false ; } ListNode* slow = head; ListNode* fast = head->next; while (slow != nullptr && fast != nullptr && fast->next != nullptr ) { if (slow == fast) return true ; slow = slow->next; fast = fast->next->next; } return false ; } };
复杂度O(n),一样12ms
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 class Solution {public : ListNode* Reverse (ListNode* head, int k) { if (k==1 )return head; ListNode* newhead=Reverse (head->next,k-1 ); head->next->next=head; head->next=nullptr ; return newhead; } ListNode* reverseKGroup (ListNode* head, int k) { ListNode*nexthead=head; if (!head)return nullptr ; for (int i=0 ;i<k;i++) { if (!nexthead) return head; nexthead=nexthead->next; } ListNode*newhead=Reverse (head,k); head->next=reverseKGroup (nexthead,k); return newhead; } };
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* Reverse (ListNode* head, int k) { if (k==1 )return head; ListNode* curr=head->next; ListNode* newhead=head->next; ListNode* prev=head; for (int i=0 ;i<k-1 ;i++) { ListNode*tmp=curr->next; curr->next=prev; prev=curr; curr=tmp; } return prev; } ListNode* reverseKGroup (ListNode* head, int k) { ListNode*nexthead=head; if (!head)return nullptr ; for (int i=0 ;i<k;i++) { if (!nexthead) return head; nexthead=nexthead->next; } ListNode*newhead=Reverse (head,k); head->next=reverseKGroup (nexthead,k); return newhead; } };
复杂度一样,只是迭代翻转用内存更少,能用递归还是递归吧,逻辑更清晰
随机链表的复制
哈希表遍历
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 : Node* copyRandomList (Node* head) { if (!head)return nullptr ; unordered_map<Node*,Node*>map; Node* now=head; while (now){ map[now]=new Node (now->val); now=now->next; } now=head; while (now) { Node* curr=map[now]; curr->next=map[now->next]; curr->random=map[now->random]; now=now->next; } return map[head]; } };
哈希表大法好,逻辑简单,性能也不太差
11ms 击败20.89%
二叉树
二叉搜索树第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 class Solution {public : int kthSmallest (TreeNode* root, int k) { stack<TreeNode*>s; TreeNode* curr=root; int count=k; while (curr||!s.empty ()) { while (curr) { s.push (curr); curr=curr->left; } curr=s.top (); s.pop (); count--; if (count==0 ) return curr->val; curr=curr->right; } return 0 ; } };
直接中序遍历就可,题解有考虑更复杂场景的操作
二叉树的右视图
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 : vector<int > rightSideView (TreeNode* root) { queue<TreeNode*>q; vector<int >res; if (!root) return res; q.push (root); while (!q.empty ()) { int num=q.size (); for (int i=0 ;i<num;i++) { TreeNode* curr =q.front (); q.pop (); if (curr->left)q.push (curr->left); if (curr->right)q.push (curr->right); if (i==num-1 )res.push_back (curr->val); } } return res; } };
层序遍历即可
二叉树展开为链表
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 : TreeNode* flat (TreeNode* root) { if (!root) return nullptr ; TreeNode *left=flat (root->left); root->left=nullptr ; TreeNode *right=flat (root->right); root->right=left; if (left) { while (left->right)left=left->right; left->right=right; } else root->right=right; return root; } void flatten (TreeNode* root) { root = flat (root); } };
居然一次写成了,按照先序的思路递归即可
0ms 击败100.00%
图
岛屿数量
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 :int dx[4 ]={1 ,0 ,-1 ,0 };int dy[4 ]={0 ,1 ,0 ,-1 };int m,n;int count;void dfs (int x,int y, vector<vector<char >>& grid,vector<vector<bool >>& visited) { visited[x][y]=true ; for (int i=0 ;i<4 ;i++) { int new_x=x+dx[i]; int new_y=y+dy[i]; bool x_in=new_x>=0 &&new_x<m; bool y_in=new_y>=0 &&new_y<n; if (x_in&&y_in&&!visited[new_x][new_y]&&grid[new_x][new_y]=='1' ) dfs (new_x,new_y,grid,visited); } } int numIslands (vector<vector<char >>& grid) { m=grid.size (); n=grid[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 (!visited[i][j]&&grid[i][j]=='1' ) { count++; dfs (i,j,grid,visited); } } return count; } };
课程表
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 canFinish (int numCourses, vector<vector<int >>& prerequisites) { int zero_count=0 ; vector<vector<int >> adj (numCourses); vector<int > in_degrees (numCourses, 0 ) ; for (const auto & pair:prerequisites) { adj[pair[1 ]].push_back (pair[0 ]); in_degrees[pair[0 ]]++; } queue<int >zero_in; for (int i=0 ;i<numCourses;i++) if (in_degrees[i]==0 ) zero_in.push (i); while (!zero_in.empty ()) { int tmp=zero_in.front (); zero_in.pop (); zero_count++; for (int next_course:adj[tmp]) { in_degrees[next_course]--; if (in_degrees[next_course]==0 ) zero_in.push (next_course); } } return zero_count==numCourses; } };
拓扑排序判断是否有环:不断剔除入度为0的点,若最后有没有剔除的节点,证明有环
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 canFinish (int numCourses, vector<vector<int >>& prerequisites) { int16_t zero_count=0 ; vector<vector<int16_t >> adj (numCourses); vector<int16_t > in_degrees (numCourses, 0 ) ; for (const auto & pair:prerequisites) { adj[pair[1 ]].push_back (pair[0 ]); in_degrees[pair[0 ]]++; } queue<int16_t >zero_in; for (int16_t i=0 ;i<numCourses;i++) if (in_degrees[i]==0 ) zero_in.push (i); while (!zero_in.empty ()) { int16_t tmp=zero_in.front (); zero_in.pop (); zero_count++; for (int16_t next_course:adj[tmp]) { in_degrees[next_course]--; if (in_degrees[next_course]==0 ) zero_in.push (next_course); } } return zero_count==numCourses; } };
全部换成int16_t,速度和内存都快了,也是逃课方法吧
实现Trie
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Trie {public : unordered_set<string>set; unordered_set<string>prefix_set; Trie () { } void insert (string word) { set.insert (word); for (int i=1 ;i<=word.size ();i++) prefix_set.insert (word.substr (0 ,i)); } bool search (string word) { return set.count (word); } bool startsWith (string prefix) { return prefix_set.count (prefix); } };
主打一个能过就行
111ms 击败5.87%
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 Trie {private : bool isEnd; Trie* next[26 ]; public : Trie () : isEnd (false ), next{} {} void insert (string word) { Trie* node=this ; for (char c:word) { if (!node->next[c-'a' ]) node->next[c-'a' ]=new Trie (); node=node->next[c-'a' ]; } node->isEnd=true ; } bool search (string word) { Trie* node=this ; for (char c:word) { node=node->next[c-'a' ]; if (!node)return false ; } return node->isEnd; } bool startsWith (string prefix) { Trie* node=this ; for (char c:prefix) { node=node->next[c-'a' ]; if (!node)return false ; } return true ; } };
40ms 击败25.67%
回溯
全排列
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<vector<int >> res; vector<int >Path; int n; void backtracking (vector<int >& nums,vector<bool >& visited) { if (Path.size ()==nums.size ()) { res.push_back (Path); return ; } for (int i=0 ;i<n;i++) { if (visited[i])continue ; Path.push_back (nums[i]); visited[i]=true ; backtracking (nums,visited); Path.pop_back (); visited[i]=false ; } } vector<vector<int >> permute (vector<int >& nums) { n=nums.size (); vector<bool > visited (n,false ) ; backtracking (nums,visited); return res; } };
0ms击败100.00%
学习了下回溯算法
子集
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 :int n;vector<vector<int >> res; vector<int >path; void backtrack (vector<int >& nums, int start,int size) { if (path.size ()>=size) { res.push_back (path); return ; } for (int i=start;i<n;i++) { path.push_back (nums[i]); backtrack (nums,i+1 ,size); path.pop_back (); } } vector<vector<int >> subsets (vector<int >& nums) { n=nums.size (); for (int i=0 ;i<=n;i++) { backtrack (nums,0 ,i); } return res; } };
3ms 击败31.64%
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 n;vector<vector<int >> res; vector<int >path; void backtrack (vector<int >& nums, int start) { res.push_back (path); for (int i=start;i<n;i++) { path.push_back (nums[i]); backtrack (nums,i+1 ); path.pop_back (); } } vector<vector<int >> subsets (vector<int >& nums) { n=nums.size (); backtrack (nums,0 ); return res; } };
原来每个节点都可以提交,0ms 击败100.00%
电话号码的字母组合
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<string> res; string path; const vector<string> mapping = { "" , "" , "abc" , "def" , "ghi" , "jkl" , "mno" , "pqrs" , "tuv" , "wxyz" }; void backtrack (string digits,int idx) { if (idx>=digits.size ()) { res.push_back (path); return ; } string letters=mapping[digits[idx]-'0' ]; for (int i=0 ;i<letters.size ();i++) { path+=letters[i]; backtrack (digits,idx+1 ); path.pop_back (); } } vector<string> letterCombinations (string digits) { backtrack (digits,0 ); return res; } };
有时考虑一下内存使用也能加快代码速度 0ms 击败100.00%
栈
每日温度
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<int > dailyTemperatures (vector<int >& temperatures) { int n=temperatures.size (); vector<int > res (n,0 ) ; stack<pair<int ,int >> wait; int count=0 ; for (int i=0 ;i<n;i++) { while (!wait.empty ()&&wait.top ().first<temperatures[i]) { res[wait.top ().second]=i-wait.top ().second; wait.pop (); } wait.push (make_pair (temperatures[i],i)); } return 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 class Solution {public : vector<int > dailyTemperatures (vector<int >& temperatures) { int n=temperatures.size (); vector<int > res (n,0 ) ; stack<int > wait; wait.push (0 ); for (int i=1 ;i<n;i++) { if (!wait.empty ()) { int idx=wait.top (); while (temperatures[idx]<temperatures[i]) { res[idx]=i-idx; wait.pop (); if (wait.empty ()) break ; idx=wait.top (); } } wait.push (i); } return res; } };
虽然复杂度都是O(N),但速度快了许多
16ms 击败90.74%
贪心
跳跃游戏
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Solution {public : bool canJump (vector<int >& nums) { int n=nums.size (); if (n==0 ) return false ; vector<bool > dp (n,false ) ; dp[0 ]=true ; for (int i=0 ;i<n;i++) for (int j=1 ;j<=nums[i];j++) { if (j+i<n&&dp[i]==true ) dp[i+j]=true ; } return dp[n-1 ]; } };
丑陋的不像动态规划的动态规划 2729ms
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Solution {public : bool canJump (vector<int >& nums) { int n=nums.size (); if (n==0 ) return false ; vector<bool > dp (n,false ) ; dp[0 ]=true ; for (int i=1 ;i<n;i++) for (int j=0 ;j<=i&&dp[j];j++) { if (i-j<=nums[j]) { dp[i]=true ; break ; } } return dp[n-1 ]; } };
应该是动态规划的极限了,2175ms
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Solution {public : bool canJump (vector<int >& nums) { int n=nums.size (); int maxposition=0 ; int nowposition=0 ; for (nowposition=0 ;nowposition<=maxposition;nowposition++) { maxposition=max (maxposition,nowposition+nums[nowposition]); if (maxposition>=n-1 ) return true ; } return false ; } };
0ms 击败100.00%
跳跃游戏2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Solution {public : int jump (vector<int >& nums) { int n=nums.size (); vector<int > dp (n,100000 ) ; dp[0 ]=0 ; for (int i=0 ;i<n&&dp[i]!=100000 ;i++) for (int j=1 ;j<=nums[i]&&i+j<n;j++) { dp[i+j]=min (dp[i]+1 ,dp[i+j]); } return dp[n-1 ]; } };
动态规划在这种要比大小的贪心不会那么差
75ms 击败11.29%
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Solution {public : int jump (vector<int >& nums) { int length=nums.size (); int maxposition =0 ; int end=0 ; int steps=0 ; for (int i=0 ;i<length-1 ;i++) { maxposition=max (maxposition,nums[i]+i); if (i==end) { end=maxposition; steps++; } } return steps; } };
动态规划
完全平方数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Solution {public : int numSquares (int n) { vector<int >dp (n+1 ,n); dp[0 ]=0 ; dp[1 ]=1 ; for (int i=2 ;i<=n;i++) for (int j=1 ;j*j<=i;j++) { dp[i]=min (dp[i],dp[i-j*j]+1 ); } return dp[n]; } };
复杂度O(n^1.5),39ms 击败90.13%
零钱兑换
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Solution {public : int coinChange (vector<int >& coins, int amount) { int n=coins.size (); vector<int > dp (amount+1 ,amount+1 ) ; dp[0 ]=0 ; for (int i=1 ;i<=amount;i++) { for (int j=0 ;j<n;j++) { if (coins[j]<=i&&dp[i-coins[j]]!=-1 ) dp[i]=min (dp[i],dp[i-coins[j]]+1 ); } if (dp[i]==amount+1 )dp[i]=-1 ; } return dp[amount]; } };
单词拆分
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 wordBreak (string s, vector<string>& wordDict) { int n=s.size (); int dict_size=wordDict.size (); vector<bool >dp (n+1 ,false ); dp[0 ]=true ; for (int i=1 ;i<=n;i++) for (int j=0 ;j<dict_size;j++) { int idx=i-wordDict[j].size (); if (idx>=0 &&idx<=n) if (dp[idx]&& s.substr (idx,i-idx)==wordDict[j]) { dp[i]=true ; break ; } } return dp[n]; } };
学到了substr的后面的是长度而不是结束位
0ms 击败100.00%