# 复杂度计算
- 电脑计算能力 4e8/s
- 若n方算法,n=me4,则达到上限
- 一般要小于数量级 同等数量级玄学过与不过
双指针
数字
bfs 279 perfect squares
/*
bfs or dpBag
value -> current sum
*/
class Solution {
public:
int numSquares(int n) {
queue<int> q;
q.emplace(0);
int res = 0;
while(!q.empty()){
++res;
int size = q.size(); //把这一层清掉
for(int k = 0; k<size; k++){
int cur = q.front();
q.pop();
for(int i = 1; cur + i*i <= n; i++){
if(cur + i*i == n){
return res;
}else{
q.emplace(cur + i*i);
}
}
}
}
return res;
}
};
26 Remove Duplicates from Sorted Array
/*
input array will be known to the caller as well.
so i need to update the nums as well.
*/
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
int slow=0,fast=1;
int n=nums.size();
if(n<2) return n;
while(fast<n){
if(nums[fast]!=nums[slow]){
nums[slow+1]=nums[fast];
slow++;
}
fast++;
}
return slow+1;
}
};
贪心
字符串
763 划分字母区间
/*
greedy method
26 small letter: use a vector as a hash table to store the last index where each letter appears
use an unordered_set to store current letter which has been visited
the rule of partition is that each letter appears in at most one part
visit a letter -> compare this index and the last index the letter appears
unorder_set is empty-> letters which has been visited cannot be visited in the following parts of the string
*/
class Solution {
public:
vector<int> partitionLabels(string S) {
vector<int> res;
vector<int> hash(26);
for(int i=0;i<S.size();i++){
hash[S[i]-'a']=i;
}
unordered_set<char> st;
int start=0,end=0;
while(end<S.size()){//condition of entering the while loop
st.insert(S[end]); //自动去重
if(hash[S[end]-'a']==end){
st.erase(S[end]);
}
end++;
if(st.size()==0){
res.push_back(end-start);
start=end; //start equals end
}
}
return res;
}
};
任务分配
252 会议室
/*
gready:
按结束时间由小到大排序(结束时间!!!)
然后从头到尾遍历 看当前的头是否>=之前的尾,
如果满足,遍历下一个,不满足 return false
这样算出来的 也是一间屋子能承办的最多的会议数
*/
class Solution {
public:
static bool cmp(vector<int> &a, vector<int> &b){
if(a[1]==b[1]){
return a[0]<b[0];
}else{
return a[1]<b[1];
}
}
bool canAttendMeetings(vector<vector<int>>& intervals) {
sort(intervals.begin(),intervals.end(),cmp);
int n=intervals.size();
if(n==0||n==1){
return true;
}
int pre=intervals[0][1];
for(int i=1;i<n;++i){
if(intervals[i][0]<pre){
return false;
}
pre=intervals[i][1];
}
return true;
}
};
253 会议室 Ⅱ
/*
首先,将vector按照首元素从小到大进行排列(首元素!!!跟一间会议室能装几个不一样)
然后,维护一个优先队列,小顶堆,首先入队排序好的vector的第一个元素的结束时间
同时遍历排好序的vector,记当前vector为v
若v[0]>=队首, 把当前的队首出队,入队v[1]
若v[0]<队首, 新入队一个元素(v[1])
直到全部遍历结束,看优先队列的元素数量
*/
class Solution {
public:
int minMeetingRooms(vector<vector<int>>& intervals) {
sort(intervals.begin(),intervals.end());
//直接sort vector 先比较v1[0] v2[0] 若相等再比较v1[1]和v2[1]
priority_queue<int,vector<int>,greater<int>> que;
int n=intervals.size();
if(n==1){
return 1;
}
que.push(intervals[0][1]);
for(int i=1;i<n;++i){
int top=que.top();
if(intervals[i][0]>top||intervals[i][0]==top){
que.pop();
}
que.push(intervals[i][1]);
}
return que.size();
}
};
图论
BFS
399 Evaluate Division
/*
bfs
邻接表
*/
class Solution {
public:
struct edge{
string v;
double value;
edge(string x,double y):v(x),value(y){}
};
vector<double> calcEquation(vector<vector<string>>& equations, vector<double>& values, vector<vector<string>>& queries) {
unordered_map<string,vector<edge>> mp;
unordered_map<string,int> hash;
int size = equations.size();
int j = 0;
for(int i=0;i<size;i++){
mp[equations[i][0]].emplace_back(edge(equations[i][1],values[i]));
mp[equations[i][1]].emplace_back(edge(equations[i][0],1/values[i]));
if(!hash.count(equations[i][0])) hash[equations[i][0]] = j++;
if(!hash.count(equations[i][1])) hash[equations[i][1]] = j++;
}
int resSize = queries.size();
vector<double> res;
queue<string> q;
bool vis[hash.size()];
memset(vis,false,sizeof(vis));
//vector<bool> vis(hash.size(),false);
for(int i = 0; i < resSize; ++i){
string from = queries[i][0];
string to = queries[i][1];
if(hash.count(from)==0||hash.count(to)==0){
res.emplace_back(-1);
continue;
}
if(from == to){
res.emplace_back(1);
continue;
}
//bfs
while(!q.empty()){
q.pop();
}
//init
//vis.resize(hash.size(),false); //resize 不改变已有值
memset(vis,false,sizeof(vis));
q.emplace(from);
vector<double> ratios(hash.size(),-1);
double rat = -1;
while(!q.empty()){
string cur = q.front();
vis[hash[cur]] = true;
q.pop();
if(cur == to){
if(ratios[hash[cur]]==-1) rat = 1;
else rat = ratios[hash[cur]];
break;
}
for(auto [v,value] :mp[cur]){
if(vis[hash[v]]) continue;
if(ratios[hash[cur]] == -1){
ratios[hash[v]] = value;
}else{
ratios[hash[v]] = ratios[hash[cur]]*value;
}
q.emplace(v);
}
}
res.emplace_back(rat);
}
return res;
}
};
207 课程表
/*
dfs
toposort(前置选课类)-- 无环则有拓扑排序
adjacency table
入度为0放入queue
遍历时入度--
*/
class Solution {
public:
vector<int> visit;
vector<vector<int>> adjacencyTable;
bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
visit.resize(numCourses, 0); //入度数
adjacencyTable.resize(numCourses);
for(auto elem :prerequisites){
adjacencyTable[elem[1]].emplace_back(elem[0]);
++visit[elem[0]];
}
queue<int> q;
for(int i = 0; i < numCourses; ++i){
if(visit[i] == 0){
q.emplace(i);
}
}
while(!q.empty()){
int cur = q.front();
q.pop();
for(auto x:adjacencyTable[cur]){
visit[x]--;
if(visit[x]==0){
q.emplace(x);
}
}
}
for(auto x:visit){
if(x>0){
return false;
}
}
return true;
}
};
课程表 Ⅱ
建图:a->b代表a为b的先决条件
/*
用一个数组来记录每个节点的入度
首先将入度为0的节点入队
把队头的节点取出压栈(res),将他的邻接点入度全部--,如果--后入度为0,将该邻接点入队
若队列空后,仍有节点入度>0,即为环
*/
/*
邻接表建图:只留存在边的点
也是一个二维的vector
第一维为起始点,每一维对应的一个vector<edge>
一个edge里存这个点可以到的重点+边的长度,在该题中长度永远为1。
*/
class Solution {
public:
struct edge
{
int v;//边的终点
int weight;//边长
edge(int x,int y):v(x),weight(y){} //写好构造函数后,后续可以直接edge(x,y)建结构体
};
vector<int> res;
vector<vector<edge>> mp;
bool flag=true;
int n;
vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) {
n=numCourses;
mp=vector<vector<edge> >(numCourses);
vector<int> indu(numCourses,0);//每个点的入度值
for(auto elem:prerequisites)
{
mp[elem[1]].push_back(edge(elem[0],1));
indu[elem[0]]++;
}
queue<int> que;
for(int i=0;i<n;i++)
{
if(indu[i]==0) que.push(i);
}
while(!que.empty())
{
res.push_back(que.front());
vector<edge> cur = mp[que.front()];
que.pop();
for(auto elem:cur)
{
indu[elem.v]--;
if(indu[elem.v]==0) que.push(elem.v);
}
}
for(auto elem:indu)
{
if(elem>0) flag=false;
}
if(!flag) return {};
return res;
}
};
407 接雨水Ⅱ
class Solution {
/*
priority_queue来动态维护木桶的外围
核心思想就是先确定木桶的外围,找到外围的最短板子后对其周围能填水的地方填水,然后更新木桶外围。
维护一个变量horizon 表明当前的海平面
*/
//https://www.cnblogs.com/grandyang/p/5928987.html
public:
struct cell{
int x;
int y;
int height;
bool operator>(const cell &a)const{ //重载大于号 :优先队列要反着来
return height > a.height; //满足这个时 当前cell < a
}
cell(int x,int y,int height):x(x),y(y),height(height){} //手写构造函数
};
// struct cmp1{
// bool operator ()(cell a,cell b){
// return a.height>b.height;//最小值优先
// }
int trapRainWater(vector<vector<int>>& height) {
int n=height.size();
int m=height[0].size();
int dir[4][2]={{1,0},{0,1},{-1,0},{0,-1}};
priority_queue<cell,vector<cell>,greater<cell> > pq;
vector<vector<bool>> visited(n,vector<bool>(m,false));
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
if(i==0||j==0||i==n-1||j==m-1){
pq.push(cell(i,j,height[i][j]));
visited[i][j]=true;
}
}
}
int horizon=0;
int res=0;
while(!pq.empty()){
cell cur = pq.top();
pq.pop();
horizon=max(horizon,cur.height); //定海平面
for(int i=0;i<4;i++){
int newx=cur.x+dir[i][0];
int newy=cur.y+dir[i][1];
if(newx<0||newx>=n||newy<0||newy>=m||visited[newx][newy]==true){
continue;
}
pq.push(cell(newx,newy,height[newx][newy]));
res+=max(0,horizon-height[newx][newy]); //>=0
visited[newx][newy]=true;
}
}
return res;
}
};
DFS
207. Course Schedule
拓扑排序
/*
dfs
toposort(前置选课类)-- 无环则有拓扑排序
adjacency table
当一个点的所有出度都已经进入processing时,该点可以finish;
若在该过程中访问到了已经进入processing的店,说明有环;
访问到finish直接略过
visit[i] == 0 not start
visit[i] == 1 processing
visit[i] == -1 finish
*/
class Solution {
public:
vector<int> visit;
vector<vector<int>> adjacencyTable;
bool ans = true;
void dfs(int i){
visit[i] = 1;
for(auto elem: adjacencyTable[i]){
if(visit[elem] == 0){
dfs(elem);
if(!ans){ //optimize
return;
}
}else if(visit[elem] == 1){
ans = false;
return;
}
}
visit[i] = -1;
}
bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
visit.resize(numCourses, 0);
adjacencyTable.resize(numCourses);
for(auto elem :prerequisites){
adjacencyTable[elem[1]].emplace_back(elem[0]);
}
for(int i = 0; i < adjacencyTable.size(); ++i){
if(ans == false) break;
if(!visit[i]){
dfs(i);
}
}
return ans;
}
};
课程表 Ⅱ
建图:a->b代表 b为a的先决条件
/*
visit数组三种状态
visit=0 未搜索
visit=1 搜索中
visit=2 已完成(入栈)
*/
/*
临界矩阵建图:会有很多0点,造成效率低
mp[i][j]=v;//从i到j有一条长度为v的边
*/
class Solution {
public:
vector<int> res;
vector<int> visit;
vector<vector<int>> mp;
bool flag=true;
int n;
vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) {
n=numCourses;
visit=vector<int>(numCourses,0);
mp=vector<vector<int> >(numCourses,vector<int>(numCourses));
for(auto elem:prerequisites) mp[elem[0]][elem[1]]=1;
for(int i=0;i<numCourses;i++)
{
if(!visit[i]) dfs(i);
}
if(!flag) return {};
return res;
}
void dfs(int i)
{
if(visit[i]==1) {flag=false;return;}
if(visit[i]==2) return;
if(visit[i]==0)
{
visit[i]=1;
for(int j=0;j<n;j++)
{
if(!mp[i][j]) continue;
dfs(j);
}
}
res.push_back(i);
visit[i]=2;
}
};
/*
visit数组三种状态
visit=0 未搜索
visit=1 搜索中
visit=2 已完成(入栈)
*/
/*
邻接表建图:只留存在边的点
也是一个二维的vector
第一维为起始点,每一维对应的一个vector<edge>
一个edge里存这个点可以到的重点+边的长度,在该题中长度永远为1。
*/
class Solution {
public:
struct edge
{
int v;//边的终点
int weight;//边长
edge(int x,int y):v(x),weight(y){} //写好构造函数后,后续可以直接edge(x,y)建结构体
};
vector<int> res;
vector<int> visit;
vector<vector<edge>> mp;
bool flag=true;
int n;
vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) {
n=numCourses;
visit=vector<int>(numCourses,0);
mp=vector<vector<edge> >(numCourses);
for(auto elem:prerequisites) mp[elem[0]].push_back(edge(elem[1],1));
for(int i=0;i<numCourses;i++) if(!visit[i]) dfs(i);
if(!flag) return {};
return res;
}
void dfs(int i)
{
if(visit[i]==1) {flag=false;return;}
if(visit[i]==2) return;
if(visit[i]==0)
{
visit[i]=1;
for(auto elem:mp[i]) dfs(elem.v);
}
res.push_back(i);
visit[i]=2;
}
};
记忆化搜索
329 矩阵中的最长递增路径
/*
维护一个memo二维数组
memo[i][j]:以matrix[i][j]为起点的最长递增路径长度(当前的)
*/
class Solution {
public:
int m;
int n;
vector<vector<int>> memo;
vector<vector<int>> mp;
int dir[4][2]={{-1,0},{0,1},{1,0},{0,-1}};
int longestIncreasingPath(vector<vector<int>>& matrix) {
mp=matrix;
m=matrix.size();
n=matrix[0].size();
memo=vector<vector<int>> (m,vector<int>(n,0)); //一种vector赋值的方式
int ans=0;
for(int i=0;i<m;i++)
for(int j=0;j<n;j++) ans=(dfs(i,j)>ans)?dfs(i,j):ans;
return ans;
}
int dfs(int i,int j)
{
/*
记忆化的核心在于:当我们调用的memo[i][j]不为0时,说明该点已经被访问过,已经更新好值了,
这时就不需要再去更新了,直接返回即可。
若为0,初始值为设为1,表示如果这个点没有合适的newi,newj的情况时,步数长只能为1。
*/
if(memo[i][j]>0) return memo[i][j];
memo[i][j]=1;
int newi,newj;
for(int k=0;k<4;k++)
{
newi=i+dir[k][0];
newj=j+dir[k][1];
if(newi>=0 && newi<m && newj>=0 && newj<n && mp[newi][newj]>mp[i][j])
memo[i][j]=max(memo[i][j],dfs(newi,newj)+1);
/*
这部分可以这样理解:当我调用dfs(i,j)时,是要求memo[i][j]的值。
此时memo[i][j]还不知道,需要通过memo[i][j]四周的点,即memo[newi][newj]的值来求。
memo[newi][newj]通过dfs调用,相当于把四个方向最大的memo[newi][newj]+1赋给了memo[i][j]
*/
}
return memo[i][j];
}
};
DP
计数DP
338 比特位技术
/*
https://leetcode-cn.com/problems/counting-bits/solution/hen-qing-xi-de-si-lu-by-duadua/
odd number i: dp[i]=dp[i-1]+1
even number i: dp[i]=dp[i/2]
*/
class Solution {
public:
vector<int> countBits(int n) {
if(n==0) return {0};
vector<int> dp(n+1);
for(int i=1;i<=n;i++){
if(i%2==1){
dp[i]=dp[i-1]+1;
}else{
dp[i]=dp[i/2];
}
}
return dp;
}
};
494 目标和
// https://zxi.mytechroad.com/blog/dynamic-programming/leetcode-494-target-sum/
class Solution {
public:
int findTargetSumWays(vector<int>& nums, int target) {
int sum=0;
for(int num:nums){
sum+=num;
}
int n = nums.size();
vector<vector<int>> dp(n+1,vector<int>(2*sum+1,0));
dp[0][sum]=1;
for(int i=0;i<n;i++){
for(int j=0;j<2*sum+1;j++){
if(dp[i][j]==0){
continue;
}
dp[i+1][j-nums[i]]+=dp[i][j];
dp[i+1][j+nums[i]]+=dp[i][j];
}
}
if(target+sum>2*sum){
return 0;
}
return dp[n][target+sum];
}
};
字符串
42 接雨水
/*
class Solution {
//左侧遍历+右侧遍历-全部体积-柱子体积
public:
int trap(vector<int>& height) {
int n=height.size();
int max1=0,max2=0,max4=0;
int x1=0,x2=0,x3=0,x4=0;
for(int i=0;i<n;i++){
max1=max(height[i],max1);
max2=max(height[n-i-1],max2);
x1+=max1;
x2+=max2;
x4+=height[i];
}
x3=max1*n;
return x1+x2-x3-x4;
}
};
*/
class Solution {
public:
int trap(vector<int>& height){
int n = height.size();
int max1 = 0, max2 = 0;
int res = 0;
vector<int> dp1(n);
vector<int> dp2(n);
for(int i = 0; i < n; ++i){
max1=max(max1,height[i]);
dp1[i]=max1;
}
for(int i = n-1; i >= 0; --i){
max2=max(max2,height[i]);
dp2[i]=max2;
}
for(int i = 0; i < n; i++){
res += min(dp1[i],dp2[i])-height[i];
}
return res;
}
};
139 word break
/*
dp[i]==true : 字符串的前i个字符可以用wordDict里的东西来拼出来
dp[i]==false : 不可以拼出来
循环:
先遍历i (0 ~ n)
后遍历j (i ~ n)
if dp[i]==true && s[i-j]在wordDict种
dp[j+1]=true
优化:
调整遍历顺序
先外层 后里层 再break
*/
class Solution {
public:
bool wordBreak(string s, vector<string>& wordDict) {
int n = s.size();
unordered_set<string>wordDictSet;
for(auto word: wordDict) {
wordDictSet.insert(word);
}
vector<bool> dp(n+1,false);
dp[0]=true;
/* 优化前
for(int i=0;i<n;++i){
for(int j=i;j<n;j++){
if(dp[i]==true && wordDictSet.count(s.substr(i,j-i+1))!=0){
dp[j+1]=true;
}
}
}
*/
for(int j=0;j<n;++j){
for(int i=0;i<=j;++i){
if(dp[i]==true && wordDictSet.count(s.substr(i,j-i+1))!=0){
dp[j+1]=true;
break;
}
}
}
return dp[n];
}
};
图
221 最大正方形
/*
2-d vector dp:
use lower right corner to represent the side length of the largest square of the current part of the matrix
dp[i][j]=min(dp[i-1][j],dp[i][j-1],dp[i-1][j-1])+1
1-d vector dp:
dp1:dp[i-1][j-1] dp2:dp[i][j-1]
dp[j]=min(dp[j-1],dp1,dp2)+1
before this cycle: dp1=dp2 dp2=dp[j]
#attention:
In order to avoid edge cases, make the dp size+1
*/
class Solution {
public:
int maximalSquare(vector<vector<char>>& matrix) {
int m = matrix.size();
int n = matrix[0].size();
int res=0;
//1-d vector
vector<int> dp(n+1,0);
int dp1=0,dp2=0;
for(int i=0;i<m;i++){
dp2=0;
for(int j=0;j<n;j++){
dp1=dp2;
dp2=dp[j+1];
if(matrix[i][j]=='1'){
dp[j+1]=min(min(dp[j],dp1),dp2)+1;
}else{
dp[j+1]=0;
}
res=max(dp[j+1],res);
}
}
/*
2-d vector
vector<vector<int>> dp(m+1,vector<int>(n+1,0));
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(matrix[i][j]=='1'){
dp[i+1][j+1]=min(min(dp[i][j+1],dp[i+1][j]),dp[i][j])+1;
}else{
dp[i+1][j+1]=0;
}
res=max(dp[i+1][j+1],res);
}
}
*/
return res*res;
}
};
最小路径和
/*
dp[i][j]=grid[i][j]+min(dp[i-1][j],dp[i][j-1]);
*/
class Solution {
public:
int minPathSum(vector<vector<int>>& grid) {
int m=grid.size();
int n=grid[0].size();
vector<vector<int>> dp(m,vector<int>(n));
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(i==0&&j==0){
dp[i][j]=grid[i][j];
}else if(i==0){
dp[i][j]=grid[i][j]+dp[i][j-1];
}else if(j==0){
dp[i][j]=grid[i][j]+dp[i-1][j];
}else{
dp[i][j]=grid[i][j]+min(dp[i-1][j],dp[i][j-1]);
}
}
}
return dp[m-1][n-1];
}
};
面试题47. 礼物的最大价值
/*
dp[i][j]:表示到达grid[i][j]处最多能拿到礼物的价值数量
dp[i][j]=max(dp[i][j-1],dp[i-1][j])+grid[i][j] (注意j-1 i-1 的边界情况))
*/
class Solution {
public:
int maxValue(vector<vector<int>>& grid) {
int m = grid.size();
int n = grid[0].size();
vector<vector<int>> dp(m,vector<int>(n));
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
if(i==0&&j==0) dp[i][j]=grid[i][j];
else if(i==0) dp[i][j]=dp[i][j-1]+grid[i][j];
else if(j==0) dp[i][j]=dp[i-1][j]+grid[i][j];
else dp[i][j]=max(dp[i][j-1],dp[i-1][j])+grid[i][j];
}
}
return dp[m-1][n-1];
}
};
62 不同路径
/*
dp[i][j]=dp[i-1][j]+dp[i][j-1];
*/
class Solution {
public:
int uniquePaths(int m, int n) {
vector<vector<int>> dp(m,vector<int>(n,0));
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(i==0&&j==0){
dp[i][j]=1;
}
else if(i==0){
dp[i][j]=dp[i][j-1];
}
else if(j==0){
dp[i][j]=dp[i-1][j];
}
else{
dp[i][j]=dp[i-1][j]+dp[i][j-1];
}
}
}
return dp[m-1][n-1];
}
};
股票问题
122 买卖股票的最佳实践 Ⅱ
/*
1.dp:
dp[i][0]表示第i天若手上无股票时的利润
dp[i][1]表示第i天若手上有股票时的利润
dp[i][0]=max{dp[i-1][0],dp[i-1][i]+prices[i]}
dp[i][1]=max{dp[i-1][1],dp[i-1][0]-prices[i]}
dp[0][0]=0
dp[0][1]=-prices[0]
求dp[n-1][0]
2.greedy
两段之间的买进卖出都可以简化成两两之间的买入卖出
*/
class Solution {
public:
int maxProfit(vector<int>& prices) {
int n=prices.size();
if(n==1) return 0;
int res=0;
for(int i=1;i<n;i++)
{
res+=(prices[i]>prices[i-1])?(prices[i]-prices[i-1]):0;
}
return res;
/*
vector<vector<int>> dp(n,vector<int>(2));
dp[0][0]=0;
dp[0][1]=-prices[0];
for(int i=1;i<n;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[n-1][0];
*/
}
};
188 买卖股票的最佳时机 IV
/*
dp[i][0~k][0/1]: 第i天,已经完成了1~k笔交易,手上有0/1笔股票,有的利润值
买进时第二个元素++
dp[i][k][0]=max(dp[i-1][k][0],dp[i-1][k][1]+prices[i])
dp[i][k][1]=max(dp[i-1][k][1],dp[i-1][k-1][0]-prices[i])
边界条件 dp[0][1~k][1]=-prices[0] 其他初始值为0
求dp[n-1][k][0]
*/
class Solution {
public:
int maxProfit(int k, vector<int>& prices) {
int n=prices.size();
if(n==1||n==0) return 0;
int dp[n][k+1][2];
memset(dp,0,sizeof(dp));
for(int i=1;i<=k;i++) dp[0][i][1]=-prices[0];
for(int i=1;i<n;i++)
{
for(int j=1;j<=k;j++)
{
dp[i][j][0]=max(dp[i-1][j][0],dp[i-1][j][1]+prices[i]);
dp[i][j][1]=max(dp[i-1][j][1],dp[i-1][j-1][0]-prices[i]);
}
}
return dp[n-1][k][0];
}
};
309 最佳买卖股票时机含冷冻期
/*
dp[i][0/1]: 第i天,手上有0/1笔股票,有的利润值
dp[i][0]=max(dp[i-1][0],dp[i-1][1]+prices[i])
dp[i][1]=max(dp[i-1][1],dp[i-2][0]-prices[i])
边界条件 dp[0][1]=-prices[0] 其他初始值为0
注意dp[1][1]的特判
求dp[n-1][0]
*/
class Solution {
public:
int maxProfit(vector<int>& prices) {
int n=prices.size();
if(n==1||n==0) return 0;
int dp[n][2];
memset(dp,0,sizeof(dp));
dp[0][1]=-prices[0];
for(int i=1;i<n;i++)
{
dp[i][0]=max(dp[i-1][0],dp[i-1][1]+prices[i]);
if(i==1) dp[i][1]=max(dp[i-1][1],-prices[i]); //第一天拥有一支股票的两种情况
else dp[i][1]=max(dp[i-1][1],dp[i-2][0]-prices[i]);
}
return dp[n-1][0];
}
};
714 买卖股票的最佳时机含手续费
/*
dp[i][0/1]: 第i天,手上有0/1笔股票,有的利润值
dp[i][0]=max(dp[i-1][0],dp[i-1][1]+prices[i]-fee)
dp[i][1]=max(dp[i-1][1],dp[i-1][0]-prices[i])
边界条件 dp[0][1]=-prices[0] 其他初始值为0
求dp[n-1][0]
*/
class Solution {
public:
int maxProfit(vector<int>& prices,int fee) {
int n=prices.size();
if(n==1||n==0) return 0;
int dp[n][2];
memset(dp,0,sizeof(dp));
dp[0][1]=-prices[0];
for(int i=1;i<n;i++)
{
dp[i][0]=max(dp[i-1][0],dp[i-1][1]+prices[i]-fee);
dp[i][1]=max(dp[i-1][1],dp[i-1][0]-prices[i]);
}
return dp[n-1][0];
}
};
树
96 Unique Binary Search Trees
/*
https://leetcode-cn.com/problems/unique-binary-search-trees/solution/bu-tong-de-er-cha-sou-suo-shu-by-leetcode-solution/
the number of unique BST of n nodes is constant (independent of each node's value)
*/
class Solution {
public:
int numTrees(int n) {
if(n==1||n==2){
return n;
}
vector<int> dp(n+1);
dp[0]=1;
dp[1]=1;
dp[2]=2;
for(int i=3;i<=n;i++){
//calucate dp[i]
for(int j=i-1;j>=0;j--){
dp[i]+=dp[j]*dp[i-1-j];
}
}
return dp[n];
}
};
数字序列
198 打家劫舍
/*
1-d vector:
dp[i] is the sum when nums[i] is the last element of nums & when the sum includes nums[i]
dp[i]=nums[i]+max(dp[i-2],dp[i-3])
dp[-3]=d[-2]=dp[-1]=0
2-d vector: use the second dimension to store status
dp[i][0]=max(dp[i-1][1],dp[i-1][0]) //sum does not include nums[i]
dp[i][1]=dp[i-1][0]+nums[i] //sum includes nums[i]
space O(1):
pre1:dp[i-1][0]
pre2:dp[i-1][1]
cur1:dp[i][0]
cur2:dp[i][1]
#if exist negative number
1-d vector:
dp[i] is the sum when nums[i] is the last element of nums & when the sum includes nums[i]
dp[i]=nums[i]+max(dp[i-2],dp[i-3],0)
dp[-3]=d[-2]=dp[-1]=0
2-d vector: use the second dimension to store status
dp[i][0]=max(dp[i-1][1],dp[i-1][0],0) //sum does not include nums[i]
dp[i][1]=max(dp[i-1][0],0)+nums[i] //sum includes nums[i]
*/
class Solution {
public:
int rob(vector<int>& nums) {
/*
//vector:
int n = nums.size();
vector<int> dp(n+3);
int res=0;
for(int i=0;i<n;i++){
dp[i+3]=nums[i]+max(dp[i+1],dp[i]);
res=max(res,dp[i+3]);
}
return res;
*/
/*
//2-d vector
int n = nums.size();
vector<vector<int>> dp(n+1,vector<int>(2));
for(int i=0;i<n;i++){
dp[i+1][0]=max(dp[i][1],dp[i][0]);
dp[i+1][1]=nums[i]+dp[i][0];
}
return max(dp[n][0],dp[n][1]);
*/
//space O(1)
int n = nums.size();
int pre1=0,pre2=0,cur1=0,cur2=0;
for(int i=0;i<n;i++){
pre1=cur1;
pre2=cur2;
cur1=max(pre1,pre2);
cur2=nums[i]+pre1;
}
return max(cur1,cur2);
}
};
238 除自身以外数组的乘积
/*
存一个左前缀积dp 存一个右前缀积dp 最后循环一遍求解
简化:右前缀积不存dp 遍历过程中求解
*/
class Solution {
public:
vector<int> productExceptSelf(vector<int>& nums) {
int n = nums.size();
vector<int> dp(n);
dp[0]=1;//dp[i]=nums[0]-nums[i-1]的积 所以初始赋dp[0]=1
for(int i=1;i<n;i++){
dp[i]=dp[i-1]*nums[i-1];
}
vector<int> res(n);
res[n-1]=dp[n-1];
int dp2=nums[n-1];
for(int i=n-2;i>=0;i--){
res[i]=dp2*dp[i];
dp2=nums[i]*dp2;
}
return res;
}
};
152 Maximum Product Subarray
class Solution {
/*
1. 同dp 最大字串和
dp[i][0] 维护包含nums[i]的连续乘积正数max
dp[i][1] 维护包含nums[i]的连续乘积负数min
注:遍历过程中遇到0,直接该dp[i][0/1]=0;
若dp[i-1][0/1]为0,dp[i][0/1]=nums[i];
取max(dp[0-n][0] max,dp[0-n][1] max)
可简化为空间O(1)
dp1 dp2 存上一位(dp[i-1])
过程维护 res
2.
从左往右 左前缀乘积 维护一个max1 如果左前缀乘积为0,前缀最左端移到下一个点,继续维护max
从右往左 右前缀成绩 维护一个max2 后续同理
max(max1,max2)
*/
public:
int maxProduct(vector<int>& nums) {
int n = nums.size();
if(n==1){
return nums[0];
}
int dp1=0,dp2=0,res=0,tmp=0;
for(int i=0;i<n;i++){
if(nums[i]>0){
dp1=max(dp1*nums[i],nums[i]);
dp2=dp2*nums[i];
}else{
tmp = dp2;
dp2=min(dp1*nums[i],nums[i]);
dp1=tmp*nums[i];
}
cout<<i<<" "<<dp1<<" "<<dp2<<endl;
res=max(max(dp2,dp1),res);
}
return res;
}
/*
int maxProduct(vector<int>& nums) {
int n = nums.size();
if(n==1){
return nums[0];
}
int max1=nums[0];
int cur1=nums[0];
int max2=nums[n-1];
int cur2=nums[n-1];
for(int i=1;i<n;i++){
if(cur1==0){
cur1=nums[i];
}else{
cur1=nums[i]*cur1;
}
max1=max(max1,cur1);
}
for(int i=n-2;i>=0;i--){
if(cur2==0){
cur2=nums[i];
}else{
cur2=nums[i]*cur2;
}
max2=max(max2,cur2);
}
return max(max1,max2);
}
*/
};
最长上升子序列
/*
dp[i]:以nums[i]为结尾的递增子序列长度
dp[i]=max(dp[j])+1 (j从0到i-1遍历 && nums[i]>num[j])
找到最大的dp[x],即为答案
O(n*n)
*/
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
vector<int> dp (nums.size());
dp[0]=1;
int maxdp=1;
for(int i=1;i<nums.size();i++)
{
dp[i]=1;
for(int j=0;j<i;j++)
{
if(nums[i]>nums[j])
dp[i]=max(dp[i],dp[j]+1);
}
maxdp=(dp[i]>maxdp)?dp[i]:maxdp;
}
return maxdp;
}
};
最长上升子串
一次遍历,保存当前max
二分
普通
34 在排序好的数组中查找元素的第一个和最后一个位置
使用stl
/*
二分
lowerbound 找下界 >=
uppbound 找上界 >
若上下界相同 说明没有该元素
拓展:找元素的出现次数-> 最后一个的位置-最前一个的位置
*/
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
auto left = lower_bound(nums.begin(),nums.end(),target);
auto right = upper_bound(nums.begin(),nums.end(),target);
if(left==right) return {-1,-1};
else return {int(left-nums.begin()),int(right-nums.begin()-1)};
}
};
手写
class Solution {
public:
int lower(vector<int>& nums, int target)
{
int l=0;
int r=nums.size();
int mid;
while(l<r)
{
mid=(l+r)/2;
if(nums[mid]<target)
l=mid+1;
else
r=mid;
}
return l;
}
int upper(vector<int>& nums, int target)
{
int l=0;
int r=nums.size();
int mid;
while(l<r)
{
mid=(l+r)/2;
if(nums[mid]<=target)
l=mid+1;
else
r=mid;
}
return l;
}
vector<int> searchRange(vector<int>& nums, int target) {
int left = lower(nums,target);
int right = upper(nums,target);
if(left==right) return {-1,-1};
else return {left,right-1};
}
};
回溯
组合问题
dfs型组合问题
- dfs的时候 如果每个节点需要vector 可以把vector存为全局变量,把dfs过程中真正需要的int放到参数中。
- 同时要注:进入dfs前更改状态(全局变量),
- 出dfs后恢复状态(全局变量)
/*
#depth first search
every node during the search has two elements:
current chosen candidates set (res) and current target (tar)
sort + start from res:[] && tar:target
In each layer of dfs, we iterate the original candidates in the order from small to big.
if tar==0 pushback res into answer
#attention:
res.push_back() -> dfs -> res.pop_back()
if(last>cur) continue;
ex. 3 cannot go to 2 can go to elements equal or larger than 3
#optimize:
set res as global varable
set the last element of res as the parameter of dfs function because this
*/
class Solution {
public:
vector<int> original;
vector<vector<int>> ans;
vector<int> res;
void dfs(int last, int tar){
if(tar==0){
ans.push_back(res);
return;
}
for(int cur:original){
if(last>cur){
continue;
}else if(tar-cur>=0){
res.push_back(cur);
dfs(cur,tar-cur);
res.pop_back();
}else{
return;
}
}
}
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
sort(candidates.begin(),candidates.end());
original = candidates;
dfs(0,target);
return ans;
}
};
排列问题
46 全排列
class Solution {
public:
void x(vector<vector<int>>& res, vector<int>& nums, int first, int last)
{
if(first==last)
{
res.push_back(nums);
return;
}
for(int i=first;i<last;i++)
{
swap(nums[first],nums[i]);
x(res,nums,first+1,last);
swap(nums[first],nums[i]);
}
}
vector<vector<int>> permute(vector<int>& nums) {
vector<vector<int>> res;
int n=nums.size();
x(res,nums,0,n);
return res;
}
};
模拟
17 电话号码的字母组合
把他想象成多叉树
记录depth 维护跳出入口
class Solution {
public:
unordered_map<int,string> hash =
{{2,"abc"},
{3,"def"},
{4,"ghi"},
{5,"jkl"},
{6,"mno"},
{7,"pqrs"},
{8,"tuv"},
{9,"wxyz"}};
vector<string> res;
int n;
string word;
string tmpres;
void backtrace(int depth){
if(depth==n) {
//cout<<"2:"<<tmpres<<endl;
res.push_back(tmpres);
return;
}
int cur = word[depth]-'0';
string curword = hash[cur];
for(int i=0;i<curword.size();i++){
tmpres+=curword[i];
//cout<<"1:"<<tmpres<<endl;
backtrace(depth+1);
//cout<<"3:"<<tmpres<<endl;
tmpres.pop_back(); //去掉最后一个
}
}
vector<string> letterCombinations(string digits) {
n=digits.size();
word = digits;
if(n==0) return {};
backtrace(0);
return res;
}
};
22 括号生成
/*
dfs:
参数列表中包含,当前(数量和当前)数量
如果当前数量都为n,则return
如果当前左边数量小于n,则可以+(
如果当前左边数量大于,则可以+)
*/
class Solution {
public:
int num;
vector<string> res;
void backtrack(string cur,int open, int close){
if(open==num&&close==num){
res.push_back(cur);
return;
}
if(open<num){
backtrack(cur+'(',open+1,close);
}
if(open>close){
backtrack(cur+')',open,close+1);
}
}
vector<string> generateParenthesis(int n) {
string cur;
num=n;
backtrack(cur,0,0);
return res;
}
};
思维
次数统计
169 Majority Element
/*
1.bit voting:
对32位的每一位,分别遍历nums里的元素,该位为0/1,出现次数超过n/2的为最终结果该位的值。
*/
/*
2.moore voting:
set 2 varable: times elem
iterate through the nums, record the current element of the nums vector by var elem, record its frequency of occurrence by var times. if a new elem appears, the times of previous element which we recorded minus 1.
if the times == 0, I set the new element as the var elem and set the times as 1.
after the loop, the existed elem is res.
*/
class Solution {
public:
int majorityElement(vector<int>& nums) {
//1.
// int res=0;
// int cur;
// int n=nums.size();
// for(int i=0;i<32;i++){
// cur=1<<i; //1向左移动
// int count=0; //记录该位1出现的次数
// for(int num:nums){
// if((num & cur)!=0){
// count++;
// if(count > n/2){
// res=res|cur;
// }
// }
// }
// }
// return res;
int elem=nums[0];
int times=1;
for(int i=1;i<nums.size();++i){
if(times==0 && nums[i]!=elem){
elem=nums[i];
times=1;
}else if(times>0 && nums[i]!=elem){
times--;
}else if(nums[i]==elem){
times++;
}
}
return elem;
}
};
229 Majority Element Ⅱ
class Solution {
/*
Moore Sorting:
elem1=0 times1=0
elem2=1 times2=0
set 4 varable
iterate through the nums, record the current element of the nums vector by var elem, record its frequency of occurrence by var times. if a new elem appears, the times of previous element which we recorded minus 1.
if the times == 0, I set the new element as the var elem and set the times as 1.
after the loop, the existed elem is res.
*/
public:
vector<int> majorityElement(vector<int>& nums) {
int elem1=0,times1=0,elem2=1,times2=0;
int n=nums.size();
vector<int> res;
for(int num:nums){
if(num==elem1){
times1++;
}else if(num==elem2){
times2++;
}else if(times1==0){
elem1=num;
times1=1;
}else if(times2==0){
elem2=num;
times2=1;
}else{
times1--;
times2--;
}
}
times1=times2=0;
for(int num:nums){
if(num==elem1){
times1++;
}else if(num==elem2){
times2++;
}
}
if(times1>n/3){
res.push_back(elem1);
}
if(times2>n/3){
res.push_back(elem2);
}
return res;
}
};
反转
9 回文数
class Solution {
public:
bool isPalindrome(int x) {
if(x<0) return false;
int n=0;
int a=x;
long res=0;
while(a)
{
res=res*10+a%10;
a=a/10;
}
return res==x;
}
};
7 整数反转
/*
*/
class Solution {
public:
int reverse(int x) {
long res=0;
while(x)
{
int cur=x%10;
x=x/10;
res=res*10+cur;
}
if(res>pow(2,31)-1||res<-pow(2,31)) return 0;
else return res;
}
};
排列
31 下一个排列
/*
1.从后往前遍历到第一个nums[i-1]<nums[i],保存i-1这个位置,后续交换使用
2.找到i-1后面比nums[i-1]大的最小值对应的位置,如果有相同值,选靠后的(这样可以使得i到n的部分单调递减)
3.交换这两个位置,这步使得交换后的i-1位置的值确定下来。
4.需要使i到n这一段最小,之前的这一段是单调递减的,因此reverse这个部分
*/
class Solution {
public:
void nextPermutation(vector<int>& nums) {
int n = nums.size();
if(n==1||n==0) return ;
int first=-1;
for(int i=n-1;i>=1;i--)
{
if(nums[i-1]<nums[i])
{
first=i-1;
break;
}
}
if(first==-1)
{
reverse(nums.begin(),nums.end());
return;
}
int last=first+1;
for(int i=first;i<n;i++)
{
if(nums[first]<nums[i] && nums[last]>=nums[i])
{
last=i;
}
}
swap(nums[first],nums[last]);
reverse(nums.begin()+first+1,nums.end());
return;
}
};
搜索
74 Search a 2D matrix
/*
O(m+n): the same as 240. Search a 2D Matrix Ⅱ
O(Logm+logn): binary search both column and row
O(log(mn)):Treat the 2D array as a 1D array+binary search
*/
/*
class Solution {
public:
bool searchMatrix(vector<vector<int>>& matrix, int target) {
if(matrix.empty()) return false;
int l=0,r=matrix.size(),mid;
while(l<r){
mid=(l+r)/2;
if(matrix[mid][0]<=target){
l=mid+1;
}else{
r=mid;
}
}
if(l==0) return false;
int i = l-1;
int j = lower_bound(matrix[i].begin(),matrix[i].end(),target)-matrix[i].begin();
if(j==matrix[i].size()){
return false;
}
if(matrix[i][j]==target){
return true;
}else{
return false;
}
}
};
*/
class Solution{
public:
bool searchMatrix(vector<vector<int>>& matrix, int target){
int w = matrix[0].size();
int l=0,r=matrix.size()*matrix[0].size(),mid;
while(l<r){
mid=(l+r)/2;
if(matrix[mid/w][mid%w]<target){
l=mid+1;
}else{
r=mid;
}
}
if(l==matrix.size()*matrix[0].size()) {
return false;
}
return (matrix[l/w][l%w] == target);
}
};
240 Search a 2D matrix Ⅱ
/*
Start from last(first) row + first(last) column, if the current value is larger than target, -–column (Because other elements in this column cannot be smaller than the current value); if smaller then ++row.
*/
class Solution {
public:
bool searchMatrix(vector<vector<int>>& matrix, int target) {
int m = matrix.size();
int n = matrix[0].size();
int i = m-1;
int j = 0;
while(i >= 0 && i < m && j >= 0 && j < n){
if(matrix[i][j] == target){
return true;
}else if(matrix[i][j] > target){
i--;
}else{
j++;
}
}
return false;
}
};
链表
反转
回文链表
/**
* 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) {}
* };
*/
/*
1.快慢指针找到链表中间
2.反转后半部分
3.两部分逐个比较
*/
class Solution {
public:
ListNode* reverse(ListNode* head)
{
if(head->next==nullptr) return head;
ListNode* pre=nullptr;
ListNode* cur=head;
ListNode* nxt=head->next;
while(nxt!=nullptr)
{
cur->next=pre;
pre=cur;
cur=nxt;
nxt=nxt->next;
}
cur->next=pre;
return cur;
}
bool isPalindrome(ListNode* head)
{
if(head==nullptr||head->next==nullptr) return true;
ListNode* fast=head;
ListNode* slow=head;
while(fast!=nullptr)
{
slow=slow->next;
fast=fast->next;
if(fast!=nullptr) fast=fast->next;
}
ListNode* head2 = reverse(slow);
while(head2!=nullptr)
{
if(head->val!=head2->val) return false;
head=head->next;
head2=head2->next;
}
return true;
}
};
排序
23 合并k个升序链表
/**
* 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) {}
* };
*/
/*
合并两个有序链表的升级版
用指针标定每个链表下一个选择的节点位置
问题的关键在于如何从所有下一个节点中最快找到最小的那个节点连上
使用优先队列(小顶堆) pair<val,listnode*> greater自动按pair的第一个元素排序,不需重写cmp
*/
class Solution {
public:
ListNode* mergeKLists(vector<ListNode*>& lists) {
ListNode *Head = new ListNode(0);
ListNode *cur = Head;
int n=lists.size();
priority_queue<pair<int,ListNode*>,vector<pair<int,ListNode*> >,greater<pair<int,ListNode*>>> que;
for(int i=0;i<n;i++)
{
if(lists[i]==nullptr) continue;
que.push(make_pair(lists[i]->val,lists[i]));
}
while(!que.empty())
{
pair<int,ListNode*> tmp = que.top();
que.pop();
cur->next=tmp.second;
cur=cur->next;
if(tmp.second->next!=nullptr)
que.push(make_pair(tmp.second->next->val,tmp.second->next));
}
return Head->next;
}
};
反转链表 Ⅱ
/**
* 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* reverseBetween(ListNode* head, int left, int right) {
if(left==right) return head;
else
{
ListNode *H =new ListNode(0);
H->next=head;
left--;
right--;
ListNode* l=H;
ListNode* r=head;
while(left--) l=l->next;
while(right--) r=r->next;
ListNode *pre=r->next;
ListNode *cur=l->next;
ListNode *nxt=cur->next;
while(nxt!=r->next)
{
cur->next=pre;
pre=cur;
cur=nxt;
nxt=nxt->next;
}
cur->next=pre;
l->next=cur;
return H->next;
}
}
};
树
DFS
538
/**
* 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:
long long sum = 0;
TreeNode* convertBST(TreeNode* root) {
if(!root) return nullptr;
convertBST(root->right);
sum += root->val;
root->val = sum;
convertBST(root->left);
return root;
}
};
437 路径总和 Ⅲ
/**
* 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) {}
* };
1.DFS
2.DP+DFS+prefixSum
*/
/*
class Solution {
public:
int res = 0;
int allSum;
//以root为根且包含root的成功数目
void dfs(TreeNode* root, int targetSum){
if(!root) return;
if(targetSum == root->val){
res++;
}
dfs(root->left, targetSum - root->val);
dfs(root->right, targetSum - root->val);
}
int pathSum(TreeNode* root, int targetSum) {
if(!root) return 0;
dfs(root,targetSum);
pathSum(root->left,targetSum); //不以root为根的情况
pathSum(root->right,targetSum);
return res;
}
};
*/
//https://leetcode-cn.com/problems/path-sum-iii/solution/lu-jing-zong-he-iii-by-leetcode-solution-z9td/
class Solution {
public:
unordered_map<long long, int> prefix;
int dfs(TreeNode *root, long long curr, int targetSum) {
if (!root) {
return 0;
}
int ret = 0;
curr += root->val;
if (prefix.count(curr - targetSum)) {
ret = prefix[curr - targetSum];
}
prefix[curr]++;
ret += dfs(root->left, curr, targetSum);
ret += dfs(root->right, curr, targetSum);
prefix[curr]--; //key point-- 保证不影响其他线路
return ret;
}
int pathSum(TreeNode* root, int targetSum) {
prefix[0] = 1;
return dfs(root, 0, targetSum);
}
};
337 打家劫舍Ⅲ
记忆化dfs(hash存储) (空间优化)
/**
* 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) {}
* };
*/
/*
用hash来存储 记忆化dfs
*/
/*
空间优化
每一层返回f[root] g[root]
*/
/*
class Solution {
public:
int res;
unordered_map <TreeNode*,int> f,g;
//f 包含root的max
//g 不包含root的max
void dfs(TreeNode* root){
if(!root) return;
dfs(root->left);
dfs(root->right);
f[root] = root->val + g[root->left] + g[root->right]; //注:hash不存在的情况直接为0
g[root] = max(f[root->left],g[root->left])+max(f[root->right],g[root->right]);
}
int rob(TreeNode* root) {
dfs(root);
return max(f[root],g[root]);
}
};
*/
class Solution {
public:
int res;
//f 包含root的max
//g 不包含root的max
void dfs(TreeNode* root, int &f, int &g){
if(!root) return;
int lf=0,lg=0,rf=0,rg=0;
dfs(root->left,lf,lg);
dfs(root->right,rf,rg);
f = root->val + lg + rg;
g = max(lf,lg)+max(rf,rg);
}
int rob(TreeNode* root) {
int f=0,g=0;
dfs(root,f,g);
return max(f,g);
}
};
124 二叉树最大路径和
/**
* 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) {}
* };
*/
/*
dfs
*/
class Solution {
public:
//当前root包含的max(一定包含root)
int res;
int maxOfFour(int x,int y,int z, int v){
return max(max(x,y),max(z,v));
}
int dfs(TreeNode* root){
int left,right;
if(!root->left){
left = 0;
}else{
left = dfs(root->left);
}
if(!root->right){
right = 0;
}else{
right = dfs(root->right);
}
int ans1 = root->val + maxOfFour(0,left,right,left+right); //当前root 四种情况
res = max(res,ans1);
int ans2 = root->val + max(0,max(left,right)); //到下轮 去掉左右都有的情况
return ans2;
}
int maxPathSum(TreeNode* root) {
res = root->val;
dfs(root);
return res;
}
};
199 二叉树的右视图
/**
* 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) {}
* };
*/
/*
方法二:dfs(根->右->左),递归过程中保存当前深度,若该节点为此深度下第一个出现的节点,则插入到res中
*/
class Solution {
public:
vector<int> res;
void dfs(TreeNode* root,int depth)
{
if(!root) return;
if(depth==res.size())//该深度下首次有节点出现
res.push_back(root->val);
dfs(root->right,depth+1);
dfs(root->left,depth+1);
}
vector<int> rightSideView(TreeNode* root) {
if(!root) return {};
dfs(root,0);
return res;
}
};
BFS
199 二叉树的右视图
/**
* 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) {}
* };
*/
/*
方法一:bfs层序遍历,每次取该层最右侧的点加入res
*/
class Solution {
public:
vector<int> rightSideView(TreeNode* root) {
if(!root) return {};
vector<int> res;
queue<TreeNode*> que;
que.push(root);
//res.push_back(root->val);
while(!que.empty())
{
int n=que.size();
while(n--)
{
TreeNode* cur=que.front();
if(cur->left) que.push(cur->left);
if(cur->right) que.push(cur->right);
que.pop();
if(n==0) //细节 最后一次是n==1判定成功,n==0进的循环
res.push_back(cur->val);
}
}
return res;
}
};
遍历
二叉树的非递归遍历
前序
/**
* 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:
vector<int> postorderTraversal(TreeNode* root) {
vector<int> res;
stack<TreeNode* > st;
while(root!=nullptr || !st.empty())
{
if(root!=nullptr)
{
res.push_back(root->val);
st.push(root);
root=root->left;
}
else
{
root=st.top()->right;
st.pop();
}
}
return res;
}
};
中序
/**
* 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:
vector<int> postorderTraversal(TreeNode* root) {
vector<int> res;
stack<TreeNode* > st;
while(root!=nullptr || !st.empty())
{
if(root!=nullptr)
{
st.push(root);
root=root->left;
}
else
{
res.push_back(st.top()->val);
root=st.top()->right;
st.pop();
}
}
return res;
}
};
后序
/**
* 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) {}
* };
*/
/*
左-右-中
等价于
中-右-左 && 最后reverse
*/
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
vector<int> res;
stack<TreeNode* > st;
while(root!=nullptr || !st.empty())
{
if(root!=nullptr)
{
res.push_back(root->val);
st.push(root);
root=root->right;
}
else
{
root=st.top()->left;
st.pop();
}
}
reverse(res.begin(),res.end());
return res;
}
};
递归
字符串
38 外观数列
/*
recursion
use two varables: times and status to record the current number and its occurance number.
*/
class Solution {
public:
string countAndSay(int n) {
if(n==1) return "1";
string cur;
cur = countAndSay(n-1);
char status=cur[0];
int times=0;
string res;
for(int i=0;i<cur.size();i++){
if(cur[i]==status){
times++;
}else{
res+=to_string(times)+status;
times=1;
status=cur[i];
}
}
res+=to_string(times)+status;
return res;
}
};
树
面试题55-Ⅱ 二叉平衡树
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
/*
思路:如果每一次都调用函数计算当前左右子树的depth差值,会递归非常多的次数
因此,可以设置一个全局变量flag,再depth的计算过程中直接比较左右子树,并改变flag
这样 只需要最开始调用一次depth
*/
class Solution {
public:
bool flag=true;
int depth(TreeNode* root)
{
if(!root) return 0;
int l=depth(root->left);
int r=depth(root->right);
if(abs(l-r)>1) flag=false;
return max(l,r)+1;
}
bool isBalanced(TreeNode* root)
{
depth(root);
return flag;
}
};
背包
完全背包
322 零钱兑换
/*
for i = 1, 2, ..., n: # 枚举前 i 个物品
for v = 0, 1, ..., V: # 枚举体积
f[i][v] = f[i-1][v]; # 不选第 i 个物品
if v >= c[i]: # 第 i 个物品的体积必须小于 v 才能选
f[i][v] = max(f[i][v], f[i-1][v-c[i]] + w[i])
return max(f[n][0...V]) # 返回前 n 个物品的最大值
*/
/*
类比背包问题: 背包总体积 :硬币总金额
物品单个体积:硬币的单个金额
每个物品的价值:1(币的数量)
背包问题是求最多的价值是多少,该题是求最少的价值是多少
*/
/*
1.完全背包(无单个物品的数量限制):
枚举体积时,正向遍历,可省去dp数组的第一维
2.01背包(每个物品1个或0个)
枚举体积时,反向遍历,可省去dp数组的第一维
*/
class Solution {
public:
int coinChange(vector<int>& coins, int amount) {
int Max = amount + 1;
vector<int> dp(amount + 1, Max);
dp[0] = 0;
for (int j = 0; j <coins.size(); ++j)
{
for (int i = 1; i <= amount; ++i)
{
if (coins[j] <= i) dp[i] = min(dp[i], dp[i - coins[j]] + 1);
}
}
return dp[amount] > amount ? -1 : dp[amount];
}
};
01背包
416 分割等和子集
/*
01背包:
dp[i][v]=max/min(dp[i-1][v],dp[i-1][v-a[i]]+w[i])
(
完全背包:
dp[i][v]=max/min(dp[i][v],dp[i-1][v-a[i]]+w[i])
区别在于等式右边的第一项,由于完全背包每个物品不限量,所以可以考虑当前物品
)
因此,当我们把上述两种背包压缩为1维的时候(即去掉dp数组的第一维),对于01背包来说,需要对v从右向左进行遍历,因为若从左向右进行遍历,后面的dp[v]会把刚刚更新过的dp[i][v-x]当作dp[i-1][v-x](因为只有一维),对于完全背包来说,需要对v从左向右进行遍历,dp[v]需要之前更新过的dp[v-x]
*/
class Solution {
public:
bool canPartition(vector<int>& nums) {
int all=0;
for(auto elem:nums) all+=elem;
if(all%2){
return false;
} else{
all/=2;
vector<bool> dp(all+1,false);
for(int i=0;i<nums.size();i++){
for(int v=all;v>0;v--){
if(v>nums[i]){
dp[v]=dp[v]||dp[v-nums[i]];
}
else if(v==nums[i]){
dp[v]=true;
}
}
}
return dp[all];
}
}
};