DFS + 回溯专题

17. 电话号码的字母组合

迭代也可以实现搜索

循环改写dfs搜索的写法:

例如

C++写法

class Solution {
public:
vector<string> letterCombinations(string digits) {
string alp[8] = {"abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
vector<string> state;
if(digits.empty()) return state;
state.push_back("");
for(int i=0;i<digits.size();i++){
char number = digits[i];
vector<string> now;
for(int j=0;j<alp[number-'2'].size();j++){
char ch = alp[number-'2'][j];
for(int k = 0;k < state.size();k++){
now.push_back(state[k] + ch);
}
}
state = now;
}
return state;
}
};

C++11+ forEach遍历,新语法写法

class Solution {
public:
vector<string> letterCombinations(string digits) {
string alp[8] = {"abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
vector<string> state;
if(digits.empty()) return state;
state.push_back("");
for(auto digit : digits){ //对digits进行遍历 取得每一个按键数字
vector<string> now;
for(auto c : alp[digit - '2']){ //对数字对应的string能表示的形式遍历
for(auto s : state){ //对state集合里的状态进行遍历
now.push_back(s + c);
}
}
state = now;
}
return state;
}
};

两种写法耗时对比:

79. 单词搜索

搜索题

考虑顺序:

1.枚举起点 n×m

2.从起点开始依次搜索下一个点的位置 上下左右走3种

3.在枚举的过程中要保证和目标单词匹配

时间复杂度 n×m × 3^k,k为单词字符串word的长度

class Solution {
public:
int n,m;
int dx[4] = {1,-1,0,0},dy[4] = {0,0,-1,1}; //上下左右4个方向
bool exist(vector<vector<char>>& board, string word) {
if(board.empty() || board[0].empty()) return false;
n = board.size(),m = board[0].size();
for(int i=0;i<n;i++){ //1.枚举起点(第一个字符向匹配的点)
for(int j=0;j<m;j++){
if(dfs(board,i,j,word,0)) return true; //2.从起点开始搜索
}
}
return false;
} bool dfs(vector<vector<char>>& board,int x,int y,string word,int k){
if(board[x][y] != word[k]) return false;
//递归出口 当最后一个字符到达
if(k == word.size() - 1) return true;
board[x][y] = '.'; //标记已访问过 用过
for(int i=0;i<4;i++){ //枚举4个方向
int nx = x + dx[i],ny = y + dy[i];
if(nx >= 0 && nx < n && ny >=0 && ny < m){
//递归搜索单词的下一个字符
if(dfs(board,nx,ny,word,k+1)){
return true;
}
}
}
board[x][y] = word[k]; //回溯
return false;
}
};

46. 全排列

全排列,先考虑顺序问题,不能重复

全排列有两种方案

第一种方案,枚举每个位置上放哪个数字,每次dfs搜索时枚举这个数标记、回溯

class Solution {
public:
int n;
vector<bool> vis;
vector<int> path;
vector<vector<int> > result; vector<vector<int>> permute(vector<int>& nums) {
n = nums.size();
vis = vector<bool>(n,false); //初始化bool容器
dfs(nums,0);
return result;
} //枚举第k个数该放哪个数字 即枚举数字
void dfs(vector<int>& nums,int k){
if(k == n){
result.push_back(path);
return;
}
//枚举当前第k个位置上该放哪个数字 枚举这个数字的下标
for(int i=0;i<n;i++){
if(!vis[i]){
vis[i] = true; //标记用过
path.push_back(nums[i]); //放入这个数
dfs(nums,k+1);
path.pop_back(); //回溯
vis[i] = false;
}
}
}
};

47. 全排列 II

与leetcode46比较,这题数字可能重复

需要判重

A.采用枚举每个数在哪个位置

B.判重的思路:

1.对nums数组进行排序

2.dfs搜索的时候需要保证值相同的数不在同一个位置上(从下一个位置开始)搜索

class Solution {
public:
int n;
vector<vector<int>> result;
vector<int> path;
vector<bool> visit; vector<vector<int>> permuteUnique(vector<int>& nums) {
n = nums.size();
visit = vector<bool>(n);
path = vector<int>(n); sort(nums.begin(),nums.end()); dfs(nums,0,0); return result;
} //对一个数枚举该放哪个位置 即枚举位置
void dfs(vector<int>& nums,int k,int start){
if(k == n){ //递归出口
result.push_back(path);
return;
}
//枚举num[k]放的位置在哪
for(int i=start;i<n;i++){ //从start开始枚举
if(!visit[i]){
visit[i] = true;
path[i] = nums[k];
//去重关键:
//对相邻重复元素枚举的位置从下一个位置开始 而不从0开始
if(k+1 <n && nums[k+1] == nums[k]){
dfs(nums,k + 1,i+1);
}else{
dfs(nums,k + 1,0);
}
visit[i] = false;
}
}
}
};

78. 子集

给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。

递归考虑顺序:按顺序,枚举每个数两种状态:选、不选

方法一:dfs

class Solution {
public:
int n;
vector<int> path;
vector<vector<int>> result;
vector<vector<int>> subsets(vector<int>& nums) {
n = nums.size();
dfs(0,nums);
return result;
}
void dfs(int k,vector<int>& nums){
if(k == n){
result.push_back(path);
return;
}
dfs(k+1,nums);
path.push_back(nums[k]);
dfs(k+1,nums);
path.pop_back();
}
};

方法2:二进制枚举:i从0到2^n-1,位置上是1的时候选,0的时候不选

class Solution {
public:
vector<vector<int>> subsets(vector<int>& nums) {
vector<vector<int>> result;
//二进制枚举 每个数代表一种方案
for(int i=0;i< 1<<nums.size() ;i++){
vector<int> now;
//枚举每一位 位数一共nums.size()
for(int j=0;j<nums.size();j++){
//如果第j位上数字为1就表示选这个数
if(i >> j & 1){
now.push_back(nums[j]);
}
}
result.push_back(now);
}
return result;
}
};

90. 子集 II

与leetcode78的区别:数字可以重复

枚举的时候,再枚举这个数的选多少个

class Solution {
public:
int n;
vector<vector<int>> result;
vector<int> path;
vector<vector<int>> subsetsWithDup(vector<int>& nums) {
n = nums.size();
sort(nums.begin(),nums.end()); //先排序
dfs(nums,0);
return result;
}
void dfs(vector<int>& nums,int k){
if(k == n){
result.push_back(path);
return;
} //统计后面与当前元素相同的个数
int cnt = 0;
while(k + cnt < n && nums[k + cnt] == nums[k]) cnt++; for(int i=0;i<=cnt;i++) {
dfs(nums,k + cnt);
path.push_back(nums[k]);
}
for(int i=0;i<=cnt;i++) path.pop_back();
}
};

216. 组合总和 III

找出所有相加之和为 n 的 k个数的组合

组合中只允许含有 1 - 9 的正整数

并且每种组合中不存在重复的数字。

class Solution {
public:
vector<vector<int>> result;
vector<int> path;
vector<vector<int>> combinationSum3(int k, int n) {
dfs(k,1,n);
return result;
} //当前还剩k个没选 选到了数字start上 总和还需要n
void dfs(int k,int start,int n){
if(k == 0){
if(n == 0) result.push_back(path);
return;
}
for(int i = start; i <= 9; i++ ){
path.push_back(i);
//剩k-1个没选 选到i+1了 总和还需要n-i
dfs(k-1,i+1,n-i);
path.pop_back();
}
}
};

剪枝:因为还需要选k个,i<=9即最大值可以替换为 i<=9 + 1 - k个

for(int i = start; i <= 10-k; i++ )

52. N皇后 II

方法一:枚举每一个坐标放不放棋子

class Solution {
public:
int ans;
vector<bool> col,row,diag,anti_diag;
int totalNQueens(int n) {
row = col = vector<bool>(n,false);
diag = anti_diag = vector<bool>(2*n,false);
ans = 0;
dfs(0,0,0,n);
return ans;
} //当前搜索到的坐标x,y 当前已选择的皇后个数
void dfs(int x,int y,int k,int n){
if(y == n) x++, y = 0;
if(x == n){
if(k == n) ans++;
return;
}
dfs(x,y+1,k,n); //不选这个位置
if(!row[x] && !col[y] && !diag[x+y] && !anti_diag[n+x-y]){
row[x] = col[y] = diag[x+y] = anti_diag[n+x-y] = true;
dfs(x,y+1,k+1,n);
row[x] = col[y] = diag[x+y] = anti_diag[n+x-y] = false;
} }
};

方法二:枚举每一行放在哪个位置,只需枚举行

37. 解数独

开三个标记数组,分别标记行、列、所在九宫格是否用过某个数

1.先初始化,统计原棋盘已经放的元素。

2.dfs搜索每一个坐标点,枚举该坐标点下能放哪个数字,要求所在行、所在列、所在九宫格都没有这个数字,

3.递归下一个子节点,递归完true就结束程序;否则回溯这个点

52和37的小结:

Dancing Links解决 精确覆盖问题

十字链表

473. 火柴拼正方形

剪枝优化题

超时代码:时间复杂度n^4

class Solution {
public:
int n;
int ave;
vector<bool> vis;
bool makesquare(vector<int>& nums) {
n = nums.size();
vis = vector<bool>(n,false);
sort(nums.begin(),nums.end());
reverse(nums.begin(),nums.end());
long long sum = 0;
int cnt = 0;
for(int i=0;i<n;i++){
sum += nums[i];
}
if(sum == 0 || sum % 4 != 0 ) return false;
for(int i=0;i<n;i++){
if(nums[i] > sum/4) return false;
if(nums[i] == sum/4) {
cnt++;
vis[i] = true;
}
}
if(cnt == 4 && n == 4) return true;
for(int i=0;i<n;i++){
if(nums[i] >= sum/4) continue;
}
ave = sum/4;
return dfs(4-cnt,0,nums);
} bool dfs(int k,int ans,vector<int>& nums){
if(k == 0){ //递归出口
for(int i=0;i<n;i++){
if(!vis[i]) return false;
}
return true;
}
if(ans > ave) return false; //剪枝 if(ans == ave) return dfs(k-1,0,nums);
//枚举这一轮选的数
for(int i=0;i<n;i++){
if(vis[i]) continue;
vis[i] = true;
if(dfs(k,ans+nums[i],nums)) return true;
vis[i] = false;
}
return false;
}
};

剪枝优化代码:

最新文章

  1. 高效率去掉js数组中重复项
  2. 转Masonry遇到的问题
  3. How can I retrieve the remote git address of a repo?
  4. 关于Backtracing中有重复元素的处理办法
  5. -_-#【乱码】URL中文参数
  6. TD配置安装方式
  7. mysql数据库封装和 分页查询
  8. ECharts模拟迁徙案例
  9. servlet入门学习之生命周期
  10. Spring Boot整合Mybatis并完成CRUD操作
  11. 移动端web自适应适配布局解决方案
  12. 2019微软Power BI 每月功能更新系列——3月Power BI 新功能学习
  13. 关于iwinfo的调试
  14. Git环境配置
  15. Ubuntu 13.04下构建Qt5开发环境
  16. mysql中把一个表的数据批量导入另一个表中
  17. mongodb分享(二)
  18. 利用itext生成pdf的简单例子
  19. poj 2406 Power Strings(kmp应用)
  20. LG2945 【[USACO09MAR]沙堡Sand Castle】

热门文章

  1. 图论——Tarjan 初步 DFS序+时间戳+欧拉序
  2. python(json 模块)
  3. pycharm(py 文件中添加作者、时间)
  4. muduo网络库源码学习————线程类
  5. Servlet --启动时创建、配置url、ServlectContext、初始化参数、获取资源
  6. 过滤idea一些不需要的文件和文件夹的显示,在使用svn的时候可以很方便的过滤不需要提交的文件
  7. Java实现功能简单的学生管理系统(附带源代码)
  8. CSS设置table样式
  9. 在Maven项目中添加代码目录下的配置文件
  10. gulp基本使用