Index LeetCode
递归应该已经不陌生了,在前面的二叉树中,我们大量使用了递归,回溯是递归的“副产品”,只要有递归的过程就会有对应的回溯过程,递归和回溯的本质都为穷举,我们通常称为“暴搜”,回溯会有撤回之前操作的功能。
回溯可以解决的问题:
- 组合问题:如何按照一定规则在N个数中找出k个数的集合?
- 切割问题:一个字符串按照一定的规则切割,有几种切割方式?
- 自己问题:一个N个数的集合中有多少符合条件的子集?
- 排列问题:N个数按一定的规则全排列,有几种排列方式?
- 期盼问题:N皇后、数独等问题。
BM55.没有重复项数字的全排列(46.全排列)[medium]
输入:nums=[1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
说明:给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。
vector<vector<int>> permute(vector<int>& nums){
vector<vector<int>> res;
vector<int> path;
vector<bool> used(nums.size(), false);
backtracking(nums, path, used, res);
return res;
}
void backtracking(const vector<int>& nums, vector<int>& path, vector<boo>& used, vector<vector<int>>& result){
if(path.size() == nums.size()){
result.push_back(path);
return;
}
for(int i=0; i<nums.size(); i++){
if(used[i]) continue;
path.push_back(nums[i]);
used[i]==true;
backtracking(nums, path, used, result);
path.pop_back();
used[i]=false;
}
}
BM56.有重复项数字的全排列(47.全排列II)[medium]
输入:nums=[1,1,2]
输出:[[1,1,2],[1,2,1],[2,1,1]]
说明:给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。
vector<vector<int>> permuteUnique(vector<int>& nums){
vector<vector<int>> res;
vector<int> path;
vector<bool> used(nums.size(), false);
sort(nums.begin(), nums.end());
backtracking(nums, path, used, res);
return res;
}
void backtracking(const vector<int>& nums, vector<int>& path, vector<bool>& used, vector<vector<int>>& result){
if(path.size() == nums.size()){
result.push_back(path);
return;
}
for(int i=0; i<nums.size(); i++){
if(used[i]) contine;
if(i>0 && nums[i]==nums[i-1] && used[i-1]==false) continue; //去重
path.push_back(nums[i]);
used[i]=true;
backtracking(nums, path, used, result);
path.pop_back();
used[i]=false;
}
}
BM57.岛屿数量(200.岛屿数量)[medium]
输入:grid = [
["1","1","1","1","0"],
["1","1","0","1","0"],
["1","1","0","0","0"],
["0","0","0","0","0"]]
输出:1
dfs+沉没此块陆地
int numIslands(vector<vector<char>>& grid){
if(grid.size() ==0 ) return 0;
int num=0;
for(int i=0; i<grid.size(); i++)
for(int j=0; j<grid[0].size(); j++){
if(grid[i][j] == '1'){
num++;
dfs(grid, i, j);
}
}
return num;
}
void dfs(vector<vector<char>>&& grid, int i, int j){
grid[i][j]='0'; //沉没此块陆地
if(i>0 && grid[i-1][j]=='1') dfs(grid, i-1, j);
if(i<grid.size()-1 && grid[i+1][j]=='1') dfs(grid, i+1, j);
if(j>0 && grid[i][j-1]=='1') dfs(grid, i, j-1);
if(j<grid[0].size()-1 && grid[i][j+1]=='1') dfs(grid, i, j+1);
}
BM58.字符串的排列(剑指Offer38.字符串的排列)[medium]
输入:s="abc"
输出:[["abc"],["acb"],["bac"],["bca"],["cab"],["cba"]]
全排列II:排序+回溯,去重
vector<string> permutation(string s){
vector<string> res;
string path;
vector<bool> used(s.size(), false);
sort(s.begin(), s.end());
backtracking(s, path, used, res);
return res;
}
void backtracking(const string& s, string & path, vector<bool>& used, vector<string>& res){
if(path.size()==s.size()){
res.push_back(path);
return;
}
for(int i=0; i<s.size(); i++){
if(used[i]) continue;
if(i>0 && s[i]==s[i-1] && !used[i-1]) continue;
path.push_back(s[i]);
used[i]=true;
backtracking(s, path, used, res);
path.pop_back();
used[i]=false;
}
}
BM59.N皇后问题(51.N皇后)[hard]
输入:n=4
输出:[[".Q..","...Q","Q...","..Q."],["..Q.","Q...","...Q",".Q.."]]
解释:皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子;如上图所示,4 皇后问题存在两个不同的解法; 1<=n<=9
vector<vector<string>> result;
//n为输入的棋盘大小
//row是当前递归到棋盘的第几行了
vector<vector<string>> solveNQueens(int n){
vector<string> chessboard(n, string(n, '.'));
backtracking(n, 0, chessboard);
return result;
}
void backtracking(int n, int row, vector<string>& chessboard){
if(row == n){
result.push_back(chessboard);
return;
}
for(int col=0; col<n; i++){
if(isVaild(row, col, chessboard, n)){ //验证合法性
chessboard[row][col]='Q'; //放置皇后
backtracking(n, row+1, chessboard);
chessboard[row][col]='.'; //回溯,撤销皇后
}
}
}
bool isValid(int row, int col, vector<string>& chessboard, int n){
for(int i=0; i< row; i++){ //检查列,由于行在递归是按行进行,每行中只有一个皇后,无需检查
if(chessboard[i][col] == 'Q') return false;
}
for(int i=row-1, j=col-1; i>=0 && j>=0; i--,j--){ //检查45度角是否有皇后
if(chessboard[i][j] == 'Q') return false;
}
for(int i=row-1, j=col+1; i>=0 && j<n; i--,j++){ //检查135度角是否有皇后
if(chessboard[i][j] == 'Q') return false;
}
return true;
}
BM60.括号生成(22.括号生成)[medium]
输入:n=3
输出:["((()))","(()())","(())()","()(())","()()()"]
说明:数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
递归
vector<string> generateParentesis(int n){
vector<string> res;
dfs(0, 0, n, "", res);
return res;
}
void dfs(int left, int right, int n, string str, vector<string>& res){
if(left>n || right>n || left<right) return;
if(left == n && right ==n){
res.push_back(str);
return;
}
dfs(left+1, right, n, str+'(', res);
dfs(left, right+1, n, str+')', res);
}
BM61.矩阵最长递增路径(329.矩阵中的最长递增路径)[hard]
输入:matrix = [[9,9,4],[6,6,8],[2,1,1]]
输出:4
解释:最长递增路径为 [1, 2, 6, 9]。
说明:定一个 m x n 整数矩阵 matrix,找出其中最长递增路径 的长度。对于每个单元格,你可以往上,下,左,右四个方向移动。你不能在对角线方向上移动或移动到边界外。
深度优先搜索
int dirs[4][2]={{-1,0},{1,0},{0,-1},{0,1}}; //记录四个方向,**注意这里是int**
int m,n;
//深度优先搜索,返回最大单元格数
int dfs(vector<vector<int>> &matrix, vector<vector<int>>& dp, int i, int j){
if(dp[i][j] != 0) //这里也很重要
return dp[i][j];
dp[i][j]++;
for(int k=0; k<4; k++){
int nexti=i+dirs[k][0];
int nextj=j+dirs[k][1];
//判断条件
if(nexti>=0 && nexti<m && nextj>=0 && nextj<n && matrix[nexti][nextj] > matrix[i][j])
dp[i][j]=max(dp[i][j], dfs(matrix, dp, nexti, nextj)+1);
}
return dp[i][j];
}
int longestIncreasingPath(vector<vector<int>>& matrix){
if(matrix.size()==0 || matrix[0].size()==0) //矩阵为空
return 0;
int res=0;
m=matrix.size();
n=matrix[0].size();
vector<vector<int>> dp(m, vector<int> (n, 0)); //从i,j处的单元格拥有的最长递增路径
for(int i=0; i<m; i++){
for(int j=0; j<n; j++){
res=max(res, dfs(matrix, dp, i, j)); //更新最大值
}
}
return res;
}
BM74.数字字符串转化成IP地址(93.复原IP地址)[medium]
输入:s = "25525511135"
输出:["255.255.11.135","255.255.111.35"]
回溯
vector<string> result; //记录结果
//startIndex:搜索的起始位置,pointNum:添加逗点的数量
void backtracking(string& s, int startIndex, int pointNum){
if(pointNum == 3){ //逗点数量为3,分割结束
//判断第四段子字符串是否合法,如果合法就放进result中
if(isVaild(s, startIndex, s.size()-1)){
result.push_back(s);
}
return;
}
for(int i=startIndex; i<s.size(); i++){
if(isVaild(s, startIndex, i)){ //判断[startIndex, i]这个区间的子串是否合法
s.insert(s.begin()+i+1, '.'); //在i后面插入一个逗点
pointNum++;
backtracking(s, i+2, pointNum); //插入逗点之后下一个子串的起始位置为i+2
pointNum--; //回溯
s.erase(s.begin()+i+1); //回溯删掉逗点
}else break; //不合法,直接结束本层循环
}
}
//判断字符串s在左闭右闭区间[start, end]所组成的数字是否合法
bool isVaild(const string& s, int start, int end){
if(start > end) return false;
if(s[start] == '0' && start != end) //0开头的数字不合法
return false;
int num=0;
for(int i=start; i<=end; i++){
if(s[i] > '9' || s[i] < '0') //遇到非数字字符不合法
return false;
num = num*10 + (s[i]-'0');
if(num > 255) return false;
}
return true;
}
vector<string> restoreIpAddresses(string s){
if(s.size()>12) return result;
bactracking(s, 0, 0);
return result;
}