一.递归
1.汉诺塔问题
面试题 08.06. 汉诺塔问题 - 力扣(LeetCode)
class Solution
{
public:
void dfs(vector<int>& a, vector<int>& b, vector<int>& c,int n) //把a经过b传到c上,传n个盘子
{
if(n==1) //出口
{
c.push_back(a.back());
a.pop_back();
return;
}
dfs(a,c,b,n-1);
c.push_back(a.back());
a.pop_back();
dfs(b,a,c,n-1);
}
void hanota(vector<int>& a, vector<int>& b, vector<int>& c)
{
dfs(a,b,c,a.size());
}
};
2.合并两个有序链表
/**
* 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* mergeTwoLists(ListNode* l1, ListNode* l2) //函数的功能是连接两个节点并返回
{
if(l1==nullptr) return l2;
if(l2==nullptr) return l1;
if(l1->val <= l2->val)
{
l1->next=mergeTwoLists(l1->next,l2);
return l1;
}
else
{
l2->next=mergeTwoLists(l1,l2->next);
return l2;
}
}
};
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* 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; //相当于后序遍历,而且newhead指向最后一个往前递归的时候就再也没变过
}
};
4.两两交换链表中的结点
/**
* 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* swapPairs(ListNode* head)
{
if(head==nullptr || head->next==nullptr) return head;
ListNode* tmp=swapPairs(head->next->next);
ListNode* next=head->next;
if(next==nullptr) return head;
next->next=head;
head->next=tmp;
return next;
}
};
5.Pow(x,n) - 快速幂
class Solution
{
public:
double myPow(double x, int n)
{
if(n==0) return 1;
if(n==INT_MIN)
{
return 1/(myPow(x,INT_MAX)*x*1.0);
}
if(n<0)
{
n=n*(-1);
double tmp=myPow(x,n/2);
if(n%2==0) return 1/(tmp*tmp*1.0);
else return 1/(tmp*tmp*x*1.0);
}
double tmp=myPow(x,n/2);
if(n%2==0) return tmp*tmp;
else return tmp*tmp*x;
}
};
二.二叉树中的深搜
1.计算布尔二叉树的值
2331. 计算布尔二叉树的值 - 力扣(LeetCode)
/**
* 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:
bool evaluateTree(TreeNode* root)
{
if(root->val==0) return false;
if(root->val==1) return true;
bool left=evaluateTree(root->left);
bool right=evaluateTree(root->right);
if(root->val==2) return (left || right);
if(root->val==3) return (left && right);
return true;
}
};
2.求根节点到叶节点数字之和
129. 求根节点到叶节点数字之和 - 力扣(LeetCode)
/**
* 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 dfs(TreeNode* root,int prevsum)
{
prevsum=prevsum*10+root->val;
if(root->left==nullptr && root->right==nullptr) return prevsum;
int sum=0;
if(root->left) sum += dfs(root->left,prevsum);
if(root->right) sum += dfs(root->right,prevsum);
return sum;
}
int sumNumbers(TreeNode* root)
{
return dfs(root,0);
}
};
3.二叉树剪
/**
* 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:
TreeNode* pruneTree(TreeNode* root)
{
if(root==nullptr) return nullptr;
root->left=pruneTree(root->left);
root->right=pruneTree(root->right);
if(root->val==0 && root->left==nullptr && root->right==nullptr)
{
delete root;
root=nullptr;
}
return root;
}
};
4.验证二叉搜索树
/**
* 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 prev=LONG_MIN; //int不够小
bool isValidBST(TreeNode* root)
{
if(root==nullptr) return true;
bool left=isValidBST(root->left);
if(left==false) return false; //剪枝1
bool cur=false;
if((root->val)>prev) cur=true;
if(cur==false) return false; //剪枝2
prev=root->val;
bool right=isValidBST(root->right);
return left&&right&&cur;
}
};
5.二叉搜索树中第k小的元素
230. 二叉搜索树中第K小的元素 - 力扣(LeetCode)
/**
* 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
{
int Min=INT_MAX;
int count=0;
public:
void dfs(TreeNode* root, int k)
{
if(root==nullptr) return ;
dfs(root->left,k);
if(count<k)
{
Min=root->val;
count++;
}
else return;
dfs(root->right,k);
}
int kthSmallest(TreeNode* root, int k)
{
dfs(root,k);
return Min;
}
};
6.二叉树的所有路径
/**
* 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<string> ans;
void dfs(TreeNode* root,string path)
{
if(root==nullptr) return ;
path += to_string(root->val);
if(root->left==nullptr && root->right==nullptr) //是叶子节点
{
ans.push_back(path);
return;
}
else
{
path += "->";
}
dfs(root->left,path);
dfs(root->right,path);
return;
}
vector<string> binaryTreePaths(TreeNode* root)
{
string path;
dfs(root,path);
return ans;
}
};
三.穷举vs暴搜vs深搜vs回溯vs剪枝
1.全排列
class Solution
{
public:
vector<vector<int>> ret;
vector<int> path;
bool check[10];
void dfs(vector<int>& nums)
{
if(path.size()==nums.size()) //递归出口
{
ret.push_back(path);
return ;
}
for(int i=0;i<nums.size();i++)
{
if(check[i]==false)
{
path.push_back(nums[i]);
check[i]=true;
dfs(nums);
//递归完之后回溯(恢复现场)
path.pop_back();
check[i]=false;
}
}
}
vector<vector<int>> permute(vector<int>& nums)
{
dfs(nums);
return ret;
}
};
2.子集
方法一:
class Solution
{
public:
vector<vector<int>> ret;
vector<int> path;
vector<vector<int>> subsets(vector<int>& nums)
{
dfs(nums,0);
return ret;
}
void dfs(vector<int> nums,int pos)
{
if(pos==nums.size())
{
ret.push_back(path);
return;
}
//选
path.push_back(nums[pos]);
dfs(nums,pos+1);
path.pop_back(); //选完之后要恢复现场
//不选
dfs(nums,pos+1);
}
};
方法二:
class Solution
{
public:
vector<vector<int>> ret;
vector<int> path;
vector<vector<int>> subsets(vector<int>& nums)
{
dfs(nums,0);
return ret;
}
void dfs(vector<int> nums,int pos)
{
ret.push_back(path); //方法二你上来就先打进去
for(int i=pos;i<nums.size();i++)
{
path.push_back(nums[i]);
dfs(nums,i+1);
path.pop_back(); //回复现场
}
}
};
因为是根据决策树按1-n的顺序遍历的,所以不会出现231这种情况
四.综合练习
1.找出所有子集的异或总和再求和
1863. 找出所有子集的异或总和再求和 - 力扣(LeetCode)
class Solution
{
public:
int sum=0;
int path=0;
int subsetXORSum(vector<int>& nums)
{
dfs(nums,0);
return sum;
}
void dfs(vector<int>& nums,int pos)
{
if(nums.size()==pos)
{
sum += path;
return;
}
//选
path ^= nums[pos];
dfs(nums,pos+1);
path ^=nums[pos];
//不选
dfs(nums,pos+1);
}
};
2.全排列II
class Solution
{
public:
vector<vector<int>> ret;
vector<int> path;
bool check[10];
void dfs(vector<int>& nums)
{
if(path.size()==nums.size())
{
ret.push_back(path);
return ;
}
for(int i=0;i<nums.size();i++)
{
if((check[i]==false)&&(i==0 || nums[i-1]!=nums[i] || check[i-1]==true)) //check[i-1]==true表示在nums[i-1]!=nums[i]的前提下
{
path.push_back(nums[i]);
check[i]=true;
dfs(nums);
path.pop_back();
check[i]=false;
}
}
}
vector<vector<int>> permuteUnique(vector<int>& nums)
{
sort(nums.begin(),nums.end()); //必须要先排序
dfs(nums);
return ret;
}
};
3.电话号码的字母组合
class Solution
{
public:
string path;
vector<string> ret;
vector<string> hash={"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
void dfs(string digits,int pos)
{
if(pos==digits.size())
{
ret.push_back(path);
return;
}
string tmp=hash[digits[pos]-'0'];
for(int i=0;i<tmp.size();i++)
{
path.push_back(tmp[i]); //只是需要普通的深度遍历,不需要判断check
dfs(digits,pos+1);
path.pop_back();
}
}
vector<string> letterCombinations(string digits)
{
if(digits.size()==0) return ret;
dfs(digits,0);
return ret;
}
};
4.括号生成
class Solution
{
public:
int left=0,right=0,n;
vector<string> ret;
string path;
void dfs()
{
if(right==n)
{
ret.push_back(path);
return;
}
if(left<n) //添加左括号 (剪枝反过来了)
{
path.push_back('(');
left++;
dfs(); //在返回出回复现场
left--;
path.pop_back();
}
if(right<left) //添加右括号 (剪枝反过来了)
{
path.push_back(')');
right++;
dfs();
right--;
path.pop_back();
}
}
vector<string> generateParenthesis(int m)
{
n=m;
dfs();
return ret;
}
};
5.组合
class Solution
{
public:
vector<vector<int>> ret;
vector<int> path;
int count=0;
int kk=0;
void dfs(int n,int pos)
{
if(count==kk)
{
ret.push_back(path);
return;
}
for(int i=pos;i<=n;i++)
{
path.push_back(i);
count++;
dfs(n,i+1);
path.pop_back();
count--;
}
}
vector<vector<int>> combine(int n, int k)
{
kk=k;
if(k>n) return ret;
dfs(n,1);
return ret;
}
};
6.目标和
class Solution
{
public:
int sum=0;
int target=0;
int ans=0;
void dfs(vector<int>& nums,int pos)
{
if(pos==nums.size())
{
if(sum==target) ans++;
return;
}
//选加
sum += nums[pos];
dfs(nums,pos+1);
sum -= nums[pos];
//选减
sum -= nums[pos];
dfs(nums,pos+1);
sum += nums[pos];
}
int findTargetSumWays(vector<int>& nums, int _target)
{
target=_target;
dfs(nums,0);
return ans;
}
};
sum作为局部变量:可以防止时间超时,而且也不用回复现场
class Solution
{
public:
int target=0;
int ans=0;
void dfs(vector<int>& nums,int pos,int sum)
{
if(pos==nums.size())
{
if(sum==target) ans++;
return;
}
//选加
dfs(nums,pos+1,sum+nums[pos]);
//选减
dfs(nums,pos+1,sum-nums[pos]);
}
int findTargetSumWays(vector<int>& nums, int _target)
{
target=_target;
dfs(nums,0,0);
return ans;
}
};
7.组合总和
class Solution
{
public:
int target=0;
vector<int> path;
vector<vector<int>> ret;
void dfs(vector<int>& nums,int pos,int sum)
{
if(sum==target)
{
ret.push_back(path);
return ;
}
if((pos==nums.size()) || (sum>target)) return ;
for(int i=pos;i<nums.size();i++)
{
path.push_back(nums[i]);
dfs(nums,i,sum+nums[i]);
path.pop_back();
}
}
vector<vector<int>> combinationSum(vector<int>& nums, int _target)
{
target=_target;
dfs(nums,0,0);
return ret;
}
};
8.字母大小写全排列
class Solution
{
public:
vector<string> ret;
string path;
void dfs(string& s,int pos)
{
if(pos==s.size())
{
ret.push_back(path);
return;
}
//不改变
path.push_back(s[pos]);
dfs(s,pos+1);
path.pop_back();
//改变
if(s[pos]>'9' || s[pos]<'0') //字符串中只有字符和数字
{
char tmp=change(s[pos]);
path.push_back(tmp);
dfs(s,pos+1);
path.pop_back();
}
}
char change(char ch)
{
if(ch>='a' && ch<='z') ch -= 32;
else if(ch>='A' && ch<='Z') ch += 32;
return ch;
}
vector<string> letterCasePermutation(string s)
{
dfs(s,0);
return ret;
}
};
9.优美的排列
class Solution
{
public:
bool check[16];
int ret;
void dfs(int pos,int n)
{
if(pos==n+1) //下标填到n,所以递归出口结束是n+1
{
ret++;
return;
}
for(int i=1;i<=n;i++)
{
if(check[i]==false && (pos%i==0 || i%pos==0))
{
check[i]=true;
dfs(pos+1,n); //是pos+1,不是i+1 ,只有求子集才用i+1
//若pos:1->2->3->... 若i: 1->1->1->...再回来
check[i]=false;
}
}
}
int countArrangement(int n)
{
dfs(1,n);
return ret;
}
};
10.N皇后
class Solution
{
public:
bool checkCol[10];
bool checkDig1[20]; //主对角线
bool checkDig2[20]; //副对角线
vector<vector<string>> ret;
vector<string> path;
int n;
void dfs(int pos) //pos表示正在遍历第几行 (pos表示行)
{
if(pos==n)
{
ret.push_back(path);
return ;
}
for(int j=0;j<n;j++) //尝试在这一行里放皇后
{
if(checkCol[j]==false && checkDig1[j-pos+n]==false && checkDig2[j+pos]==false)
{
path[pos][j]='Q';
checkCol[j]=true;
checkDig1[j-pos+n]=true;
checkDig2[j+pos]=true;
dfs(pos+1); //进到下一行
path[pos][j]='.';
checkCol[j]=false;
checkDig1[j-pos+n]=false;
checkDig2[j+pos]=false;
}
}
}
vector<vector<string>> solveNQueens(int _n)
{
n=_n;
path.resize(n);
for(int i=0;i<n;i++) path[i].append(n,'.'); //初始化棋盘
dfs(0);
return ret;
}
};
11.有效的数独
class Solution
{
public:
bool row[9][10];
bool col[9][10];
bool grid[3][3][10];
bool isValidSudoku(vector<vector<char>>& board)
{
for(int i=0;i<9;i++)
{
for(int j=0;j<9;j++)
{
if(board[i][j]!='.')
{
int num=board[i][j]-'0';
if(row[i][num] || col[j][num] || grid[i/3][j/3][num]) return false;
row[i][num]=true;
col[j][num]=true;
grid[i/3][j/3][num]=true;
}
}
}
return true;
}
};
12.解数独
注意剪枝的重要性
class Solution
{
public:
bool row[9][10];
bool col[9][10];
bool grid[3][3][10];
bool dfs(vector<vector<char>>& board)
{
for(int i=0;i<9;i++)
{
for(int j=0;j<9;j++)
{
if(board[i][j]=='.')
{
//填数
for(int num=1;num<=9;num++)
{
if(row[i][num]==false && col[j][num]==false && grid[i/3][j/3][num]==false)
{
board[i][j] = '0'+num;
row[i][num] = col[j][num] = grid[i/3][j/3][num]=true;
if(dfs(board)==true) return true; //防止在错误的道路上越走越远,减少消耗(剪枝)
board[i][j]='.'; //回复现场
row[i][num] = col[j][num] = grid[i/3][j/3][num]=false;
}
}
return false;
}
}
}
return true; //填完了
}
void solveSudoku(vector<vector<char>>& board)
{
//初始化
for(int i=0;i<9;i++)
{
for(int j=0;j<9;j++)
{
if(board[i][j]!='.')
{
int num=board[i][j]-'0';
row[i][num] = col[j][num] = grid[i/3][j/3][num]=true;
}
}
}
dfs(board);
}
};
13.单词搜索
class Solution
{
public:
int m,n;
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
bool vis[7][7]={false};
bool dfs(vector<vector<char>>& board,int i,int j,string& s,int pos)
{
if(pos==s.size()) return true;
for(int k=0;k<4;k++)
{
int x=i+dx[k],y=j+dy[k];
if(x>=0 && x<m && y>=0 && y<n && vis[x][y]==false && board[x][y]==s[pos])
{
vis[x][y]=true;
if(dfs(board,x,y,s,pos+1)) return true;
vis[x][y]=false;
}
}
return false;
}
bool exist(vector<vector<char>>& board, string word)
{
m=board.size();
n=board[0].size();
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
if(board[i][j]==word[0])
{
vis[i][j]=true;
if(dfs(board,i,j,word,1)) return true;
else vis[i][j]=false;
}
}
}
return false;
}
};
14.黄金矿工
class Solution
{
public:
int m;
int n;
bool vis[16][16]={false};
int ret=0;
int maxret=0;
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
void dfs(vector<vector<int>>& grid,int i,int j)
{
maxret=max(maxret,ret); //有可能自己一个就是最大的
for(int k=0;k<4;k++)
{
int x=i+dx[k],y=j+dy[k];
if(x>=0 && x<m && y>=0 && y<n && vis[x][y]==false && grid[x][y]!=0)
{
int tmp=grid[x][y];
ret += grid[x][y];
grid[x][y]=0;
vis[x][y]=true;
dfs(grid,x,y);
grid[x][y]=tmp;
ret -= grid[x][y];
vis[x][y]=false;
}
}
}
int getMaximumGold(vector<vector<int>>& grid)
{
m=grid.size();
n=grid[0].size();
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
if(grid[i][j]!=0)
{
ret = grid[i][j];
vis[i][j]=true;
dfs(grid,i,j); //从ij开始dfs时没包括自己,注意把他自己加上,还要注意回复现场与改变vis数组
vis[i][j]=false;
}
}
}
return maxret;
}
};
15.不同路径III
class Solution
{
public:
int count=1; //起点自己本身占一个格子
int ret=0;
int m,n;
int sum; //0的格子个数
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
bool vis[20][20];
void dfs(vector<vector<int>>& grid,int i,int j)
{
if(grid[i][j]==2) //走到终点了
{
if(count==sum) ret++;
return;
}
for(int k=0;k<4;k++)
{
int x=i+dx[k],y=j+dy[k];
if(x>=0 && x<m && y>=0 && y<n && vis[x][y]==false && grid[x][y]!=-1) //只要不等于-1就行
{
count++;
vis[x][y]=true;
dfs(grid,x,y);
count--;
vis[x][y]=false;
}
}
}
int uniquePathsIII(vector<vector<int>>& grid)
{
m=grid.size();
n=grid[0].size();
int beginx,beginy;
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
if(grid[i][j]==0) sum++;
else if(grid[i][j]==1)
{
beginx=i;
beginy=j;
}
}
}
sum += 2; //还要算上起点和终点
vis[beginx][beginy]=true;
dfs(grid,beginx,beginy);
return ret;
}
};
五.记忆化搜索(递归中的dp)
1.斐波那契数
class Solution
{
public:
int memo[31]; //备忘录
int dfs(int n)
{
//3.每次进入都先往备忘录里查找一下
if(memo[n] != -1)
{
return memo[n];
}
if(n==1 || n==0)
{
memo[n]=n; //2.返回之前把值存到备忘录里面
return memo[n]; //然后仅需返回备忘录的值
}
memo[n]=dfs(n-1)+dfs(n-2); //2.返回之前把值存到备忘录里面
return memo[n]; //然后仅需返回备忘录的值
}
int fib(int n)
{
//1.初始化备忘录
memset(memo,-1,sizeof(memo));
return dfs(n);
}
};