classSolution { public: voidmoveZeroes(vector<int>& nums){ auto l = nums.begin(); auto r = nums.begin(); while (r != nums.end()){ if (*r != 0){ int temp = *l; *l = *r; *r = temp; ++l; } ++r;
} } };
11. 盛最多水的容器
给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。返回容器可以储存的最大水量。说明:你不能倾斜容器。
classSolution(object): defthreeSum(self, nums): """ :type nums: List[int] :rtype: List[List[int]] """ res = [] nums_dict = defaultdict(int) for num in nums: nums_dict[num] += 1 nums_list = list(nums_dict.keys()) nums_list.sort()
if nums_dict.get(0, 0) > 2: res.append([0, 0, 0]) for left in nums_list: if left >= 0: break for right inreversed(nums_list): if right <= 0: break medium = -left-right if medium in nums_dict: if medium == left or medium == right: if nums_dict[medium] > 1: res.append([left, medium, right]) elif medium > left and medium < right: res.append([left, medium, right]) return res
classSolution { public: vector<vector<int>> threeSum(vector<int>& nums) { vector<vector<int>> res = {}; sort(nums.begin(),nums.end()); auto bg = nums.begin(); auto ed = nums.end(); for (auto p = bg; p<ed-2;){ auto l = p+1, r = ed - 1; int k = *p; while(l < r){ while(*l+*r<-k&&l<r) l++; while(*l+*r>-k&&l<r) r--; if(l>=r) break; if (*l+*r==-k){ res.push_back({*p,*l,*r}); int x=*l,y=*r; while(*l==x&&l<r)l++; while(*r==y&&l<r)r--; } } while(*p==k&&p<ed-2)p++; } return res; } };
42. 接雨水
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
解法:每个柱子上存储的雨量由leftmax和rightmax较低者决定,双指针向中间走,不断更新leftmax和rightmax。对于left,leftmax一定准确;对于right,rightmax一定准确。故只需判断当前leftmax和rightmax的大小,即可判断是更新left还是right准确。更新后调整索引位置变化1即可,时间复杂度O(n),空间复杂度O(1)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
classSolution(object): deftrap(self, height): """ :type height: List[int] :rtype: int """ l,r=0,len(height)-1 lmax,rmax=height[0],height[-1] ans = 0 while l<r: if lmax<rmax: ans+=lmax-height[l] l+=1 lmax=max(lmax,height[l]) else: ans+=rmax-height[r] r-=1 rmax = max(rmax,height[r]) return ans
classSolution(object): deflengthOfLongestSubstring(self, s): """ :type s: str :rtype: int """ occ =set() l,r=0,0 k=0 while r < len(s): if s[r] in occ: while s[r] in occ: occ.remove(s[l]) l+=1 occ.add(s[r]) r+=1 k=max(k,r-l) return k
classSolution { public: vector<int> findAnagrams(string s, string p){ int slen=s.size(),plen=p.size(); if (slen<plen){ returnvector<int>(); } vector<int> ans; vector<int> count(26); for (int i=0;i<plen;++i){ ++count[s[i]-'a']; --count[p[i]-'a']; } int differ = 0; for (int i=0;i<26;++i){ if (count[i]!=0){ ++differ; } } if (differ==0){ ans.emplace_back(0); } for (int i=0;i<slen-plen;++i){ //窗口少了字母s[i],引起变化,更新count和differ --count[s[i]-'a']; if (count[s[i]-'a']==0){ --differ; } elseif (count[s[i]-'a']==-1){ ++differ; }
//窗口多了字母s[i+p],引起变化,更新count和differ ++count[s[i+plen]-'a']; if (count[s[i+plen]-'a']==1){ ++differ; } elseif (count[s[i+plen]-'a']==0){ --differ; } if (differ == 0){ ans.emplace_back(i+1); } } return ans;
} };
子串
560. 和为 K 的子数组
给你一个整数数组 nums 和一个整数 k ,请你统计并返该数组中和为k的子数组的个数 。子数组是数组中元素的连续非空序列。
解法:暴力枚举会超时,因此可以在遍历时统计nums[:j]的和sum,使用sum[j]-k=sum[i]来判断是否存在nums[j:i]的和为k的子数组,数量为sum[j]-k出现的次数。时间复杂度由O(n^2)变为O(n),空间复杂度由O(1)变为O(n)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
classSolution(object): defsubarraySum(self, nums, k): """ :type nums: List[int] :type k: int :rtype: int """ prefix_count = {0: 1} # 储存前缀和出现的次数 current_sum = 0# 存储当前前缀和 count = 0# 统计和为k的子数组数量 for num in nums: current_sum += num if current_sum - k in prefix_count: count += prefix_count[current_sum - k] prefix_count[current_sum] = prefix_count.get(current_sum, 0) + 1 return count
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
classSolution { public: intsubarraySum(vector<int>& nums, int k){ unordered_map<int,int> mp; mp[0]=1; int ans=0,pre=0; for (auto &num:nums){ pre += num; if (mp.find(pre-k) != mp.end()){ ans +=mp[pre-k]; } mp[pre]++;
} return ans; } };
239. 滑动窗口最大值
给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。返回 滑动窗口中的最大值 。
解法: 滑动过程中只需用一个deque在队首维护最大值即可,队列里不需要完整的排序。只需保证:新元素加入时,删除所有比它小的“无用元素”;确保窗口滑动时,队首如果过期就弹出。
from collections import deque classSolution: defmaxSlidingWindow(self, nums: List[int], k: int) -> List[int]: res=[] n = len(nums) q = deque() for i inrange(k): while q and nums[q[-1]]<=nums[i]: q.pop() q.append(i) res.append(nums[q[0]]) for i inrange(k,n): while q and nums[q[-1]]<=nums[i]: q.pop() q.append(i) while q[0] <= i-k: q.popleft() res.append(nums[q[0]]) return res
classSolution(object): defmaxSubArray(self, nums): """ :type nums: List[int] :rtype: int """ t=0 ma=float("-inf") for i in nums: t=t+i if t>ma: ma=t if t<0: t=0 return ma
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
classSolution { public: intmaxSubArray(vector<int>& nums){ int t=0,ma=std::numeric_limits<int>::min(); for(auto & num: nums){ t+=num; if (t>ma){ ma=t; } if (t<0){ t=0; } } return ma; } };
C++中可以用 std::sort(v.begin(), v.end(), cmp) 排序,默认按 < 运算符排序,也可自定义 cmp 函数。
C++ 匿名函数可用 [](const int& a, const int& b) { return a < b; } 表示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
classSolution(object): defmerge(self, intervals): """ :type intervals: List[List[int]] :rtype: List[List[int]] """ intervals.sort(key=lambda x:x[0]) ans = [] for i,j in intervals: ifnot ans or ans[-1][1] < i: ans.append([i,j]) else: ans[-1][1]=max(ans[-1][1],j) return ans
classSolution(object): defrotate(self, nums, k): """ :type nums: List[int] :type k: int :rtype: None Do not return anything, modify nums in-place instead. """ n= len(nums) k = k % n # 防止 k 大于 n self.reverse(nums,0,n-1) self.reverse(nums,0,k-1) self.reverse(nums,k,n-1) defreverse(self,nums,start,end): while start<end: nums[start],nums[end]=nums[end],nums[start] start+=1 end-=1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
classSolution { public: voidrotate(vector<int>& nums, int k){ k %= nums.size(); reverse(nums, 0, nums.size() - 1); reverse(nums, 0, k - 1); reverse(nums, k, nums.size() - 1); } voidreverse(vector<int>& nums, int start, int end){ while (start < end) { swap(nums[start], nums[end]); start += 1; end -= 1; } } };
classSolution(object): defproductExceptSelf(self, nums): """ :type nums: List[int] :rtype: List[int] """ a = [0]*len(nums) b= [0]*len(nums) a[0]=nums[0] b[-1]=nums[-1] for i inrange(1,len(nums)): a[i]=a[i-1]*nums[i] for i inrange(len(nums)-2,-1,-1): b[i]=b[i+1]*nums[i] return [b[1]]+[a[i-1]*b[i+1] for i inrange(1,len(nums)-1)]+[a[-2]]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
classSolution { public: vector<int> productExceptSelf(vector<int>& nums){ int n = nums.size(); vector<int> ans(n); ans[0]=nums[0]; for (int i=1;i<n;++i){ ans[i]=ans[i-1]*nums[i]; } int r=1; for (int i=n-1;i>0;--i){ ans[i]=ans[i-1]*r; r*=nums[i]; } ans[0]=r; return ans; } };
classSolution(object): deffirstMissingPositive(self, nums): """ :type nums: List[int] :rtype: int """ n=len(nums) for i inrange(len(nums)): if nums[i] <1: nums[i]=n+1 for i inrange(len(nums)): a = abs(nums[i]) if a<n+1: nums[a-1]=-abs(nums[a-1]) for i inrange(len(nums)): if nums[i]>0: return i+1 return n+1
1 2 3 4 5 6 7 8 9 10 11 12 13 14
classSolution { public: intfirstMissingPositive(vector<int>& nums){ for(int i = 0; i < nums.size(); i++){ while(nums[i]>=1 && nums[i] <=nums.size() &&nums[i] != nums[nums[i] - 1] ){ swap(nums[i], nums[nums[i] - 1]); } } for(int i = 0; i < nums.size(); i++){ if(nums[i] != i + 1) return i + 1; } return nums.size() + 1; } };
矩阵
73. 矩阵置零
给定一个 m x n 的矩阵,如果一个元素为 0 ,则将其所在行和列的所有元素都设为 0 。请使用原地算法。
解法:遍历矩阵,记录元素为0的行和列,然后再遍历一次置为0.如果使用矩阵第一行和第一列进行记录,使用额外变量记录第一行或者第一列是否含0,则空间复杂度降为O(1).
classSolution(object): defsetZeroes(self, matrix): """ :type matrix: List[List[int]] :rtype: None Do not return anything, modify matrix in-place instead. """ m,n=len(matrix),len(matrix[0]) temp = [] for i inrange(m): for j inrange(n): if matrix[i][j]==0: temp.append((i,j)) for i, j in temp: matrix[i][:] = [0] * n # 设置第 i 行全为 0 for row in matrix: row[j] = 0
classSolution { public: voidsetZeroes(vector<vector<int>>& matrix){ int m=matrix.size(),n=matrix[0].size(); vector<vector<int>> temp; for (int i = 0; i < m; ++i) { for (int j = 0; j < n; ++j) { if (!matrix[i][j]) { temp.push_back({i,j}); } } } for (auto& v:temp) { for (int i = 0; i < m; ++i){ matrix[i][v[1]]=0; }
for (int i = 0; i < n; ++i){ matrix[v[0]][i]=0; } } } };
54. 螺旋矩阵
给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。
解法:
1.模拟贪吃蛇,遇到边界就顺时针转向,同时将已经探索的节点标记为边界,空间复杂度O(mn)
2.从外层内缩、逐层遍历,每一层从左上角顺时针绕圈遍历,空间复杂度O(1),推荐
classSolution(object): defspiralOrder(self, matrix): """ :type matrix: List[List[int]] :rtype: List[int] """ ans = [] left,right,top,bottom=0,len(matrix[0])-1,0,len(matrix)-1 while left<=right and top <= bottom: for column inrange(left,right+1): ans.append(matrix[top][column]) for row inrange(top+1,bottom+1): ans.append(matrix[row][right]) if left < right and top < bottom: for column inrange(right-1,left,-1): ans.append(matrix[bottom][column]) for row inrange(bottom,top,-1): ans.append(matrix[row][left]) top+=1 bottom-=1 left+=1 right-=1 return ans
int rows = matrix.size(), columns = matrix[0].size(); vector<int> order; int left = 0, right = columns - 1, top = 0, bottom = rows - 1; while (left <= right && top <= bottom) { for (int column = left; column <= right; column++) { order.push_back(matrix[top][column]); } for (int row = top + 1; row <= bottom; row++) { order.push_back(matrix[row][right]); } if (left < right && top < bottom) { for (int column = right - 1; column > left; column--) { order.push_back(matrix[bottom][column]); } for (int row = bottom; row > top; row--) { order.push_back(matrix[row][left]); } } left++; right--; top++; bottom--; } return order; } };
48. 旋转图像
给定一个 n × n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。
你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。
解法:
1.每个元素直接相对中心旋转90度,临时变量存储覆盖掉的值,从左上角开始旋转,经过四次旋转,最终替换掉的值将回到左上角。只需遍历四分之一的区域,即可覆盖所有元素的旋转。技巧第i行j列的元素将旋转到倒数第i列(正数n-i-1列)第j行的位置即(i,j)->(j,n-i-1),取左上角区域n/2 x (n+1)/2 .
2.每个元素直接相对中心旋转90度等效于上下翻转一次+沿着主对角线翻转一次,可推广至所有元素。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
classSolution(object): defrotate(self, matrix): """ :type matrix: List[List[int]] :rtype: None Do not return anything, modify matrix in-place instead. """ n = len(matrix) #上下翻转 for i inrange(n//2): for j inrange(n): matrix[i][j], matrix[n - i - 1][j] = matrix[n - i - 1][j], matrix[i][j] # 主对角线翻转 for i inrange(n): for j inrange(i): matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j]
classSolution { public: voidrotate(vector<vector<int>>& matrix){ int n= matrix.size(); for (int i = 0; i < n / 2; ++i) { for (int j = 0; j < (n + 1) / 2; ++j) { // int temp = matrix[i][j]; // swap(temp,matrix[j][n-i-1]); // swap(temp,matrix[n-i-1][n-j-1]); // swap(temp,matrix[n-j-1][i]); // matrix[i][j]=temp; int temp = 0,x=j,y=i; for (int k=0;k<5;++k){ swap(temp,matrix[y][x]); swap(x,y); x=n-x-1; } } } } };
240. 搜索二维矩阵 II
编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性:每行的元素从左到右升序排列。每列的元素从上到下升序排列。
解法:从右上角开始,如果当前值大于taget,排除当前列向左移动;如果当前值小于target,排除当前行向下移动;如果当前值等于target,返回true。如果矩阵遍历完仍未找到,返回false。
m, n = len(matrix), len(matrix[0]) # 从右上角开始查找 i, j = 0, n - 1 while i < m and j >= 0: if matrix[i][j] == target: returnTrue elif matrix[i][j] > target: j -= 1# 排除当前列 else: i += 1# 排除当前行 returnFalse
classSolution { public: boolsearchMatrix(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) { returntrue; } elseif (matrix[i][j] > target) { --j; // 排除当前列 } else { ++i; // 排除当前行 } }
classSolution(object): defisPalindrome(self, head): """ :type head: Optional[ListNode] :rtype: bool """ #计数或者快慢指针找到一半的位置 n = 0 current = head while current: n+=1 current = current.next # 翻转前半部分 current = head last,next = None,None for i inrange(0,n//2): next = current.next current.next=last last = current current=next # 判断是否回文 if n%2: l,r=last,current.next else: l,r=last,current ans = True while l and r: if l.val != r.val: ans = False break l,r=l.next,r.next # 还原链表 last,next = current,None current = last while current: next = current.next current.next = last last = current current = next return ans
classSolution(object): defhasCycle(self, head): """ :type head: ListNode :rtype: bool """ p = q = head while q and q.next: p = p.next q = q.next.next if p == q: returnTrue returnFalse
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
classSolution { public: boolhasCycle(ListNode *head){ ListNode* slow = head; ListNode* fast = head; while (fast && fast->next){ slow=slow->next; fast=fast->next->next; if (fast == slow){ returntrue; } } returnfalse; } };
classSolution(object): defdetectCycle(self, head): """ :type head: ListNode :rtype: ListNode """ p = q = head while q and q.next: p = p.next q = q.next.next if p == q: q=head while q != p: q=q.next p = p.next return q returnNone
classSolution(object): defremoveNthFromEnd(self, head, n): """ :type head: Optional[ListNode] :type n: int :rtype: Optional[ListNode] """ fast = head target = head
# 提前走 n + 1 步 for _ inrange(n + 1): if fast: fast = fast.next else: # 如果 fast 提前走不了 n+1 步,说明要删的是第一个节点 return head.next
# fast 与 target 同时前进,直到 fast 到尾部 while fast: target = target.next fast = fast.next
classSolution(object): defreverseKGroup(self, head, k): """ :type head: Optional[ListNode] :type k: int :rtype: Optional[ListNode] """ n=0 temp=head while temp: n+=1 temp=temp.next if n==k: break if n<k: return head current =head last,next=None,None for i inrange(k): next=current.next current.next =last last = current current = next head.next=self.reverseKGroup(current,k) return last
classSolution: defcopyRandomList(self, head: 'Optional[Node]') -> 'Optional[Node]': ifnot head: returnNone mp ={} p = head while p: mp[p]=Node(p.val) p = p.next p = head while p: if p.next: mp[p].next=mp[p.next] if p.random: mp[p].random=mp[p.random] p = p.next return mp[head]
classSolution: defsortList(self, head: Optional[ListNode]) -> Optional[ListNode]: k =1 n = 0 h=head while h: n+=1 h = h.next res = ListNode(0) res.next = head while k < n: pre,current=res,res.next while current: h1,i= current,k while i and current: current = current.next i -=1 if i: break h2,i= current,k while current and i: i-=1 current = current.next k1,k2=k,k-i while k1 or k2: if k1==0: pre.next = h2 k2-=1 h2 = h2.next elif k2==0: pre.next = h1 k1-=1 h1=h1.next elif h1.val<h2.val: pre.next = h1 k1-=1 h1=h1.next else: pre.next = h2 k2-=1 h2 = h2.next pre = pre.next pre.next = current k*=2 return res.next
classSolution(object): defmergeKLists(self, lists): """ :type lists: List[Optional[ListNode]] :rtype: Optional[ListNode] """ heap = [] for i in lists: if i: heapq.heappush(heap,(i.val,i)) dummy = ListNode() ans = dummy while heap: f = heapq.heappop(heap) ans.next = f[1] ans = ans.next if f[1].next: heapq.heappush(heap,(f[1].next.val,f[1].next)) return dummy.next
/** * Your LRUCache object will be instantiated and called as such: * LRUCache* obj = new LRUCache(capacity); * int param_1 = obj->get(key); * obj->put(key,value); */