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;
}
};

剪枝优化代码:

LeetCode DFS搜索与回溯专题的更多相关文章

  1. Leetcode之深度优先搜索&回溯专题-491. 递增子序列(Increasing Subsequences)

    Leetcode之深度优先搜索&回溯专题-491. 递增子序列(Increasing Subsequences) 深度优先搜索的解题详细介绍,点击 给定一个整型数组, 你的任务是找到所有该数组 ...

  2. Leetcode之深度优先搜索&回溯专题-980. 不同路径 III(Unique Paths III)

    Leetcode之深度优先搜索&回溯专题-980. 不同路径 III(Unique Paths III) 深度优先搜索的解题详细介绍,点击 在二维网格 grid 上,有 4 种类型的方格: 1 ...

  3. Leetcode之深度优先搜索&回溯专题-679. 24 点游戏(24 Game)

    Leetcode之深度优先搜索&回溯专题-679. 24 点游戏(24 Game) 深度优先搜索的解题详细介绍,点击 你有 4 张写有 1 到 9 数字的牌.你需要判断是否能通过 *,/,+, ...

  4. Leetcode之深度优先搜索&回溯专题-638. 大礼包(Shopping Offers)

    Leetcode之深度优先搜索&回溯专题-638. 大礼包(Shopping Offers) 深度优先搜索的解题详细介绍,点击 在LeetCode商店中, 有许多在售的物品. 然而,也有一些大 ...

  5. 蓝桥杯dfs搜索专题

    2018激光样式 #include<bits/stdc++.h> using namespace std; /* dfs(i) 第i个激光机器 有两种选择:vis[i-1] == 0 时 ...

  6. 图解Leetcode组合总和系列——回溯(剪枝优化)+动态规划

    Leetcode组合总和系列--回溯(剪枝优化)+动态规划 组合总和 I 给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 ...

  7. nyist oj 19 擅长排列的小明(dfs搜索+STL)

    擅长排列的小明 时间限制:1000 ms  |  内存限制:65535 KB 难度:4 描写叙述 小明十分聪明.并且十分擅长排列计算.比方给小明一个数字5,他能立马给出1-5按字典序的全排列,假设你想 ...

  8. hdu 1312:Red and Black(DFS搜索,入门题)

    Red and Black Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Tot ...

  9. [ZOJ 1011] NTA (dfs搜索)

    题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=1011 题目大意:在一棵树上,给你起始状态,问你能否到达终止状态. ...

随机推荐

  1. Linux下创建 code diff 和 合并 patch

    Linux 下经常需要给别人提供 patch 以及合 patch,这时需要用到 Linux 的 diff 和 patch 命令. 1. diff 命令 diff 命令常用来比较文件.目录,也可以用来制 ...

  2. Leetcode---Solutions&Notes

    Leetcode已经成为面试必备技能之一,为了紧随潮流,也模仿大佬们刷刷题. 1.采用"龟系"做法,每道题尽量做到时间复杂度和空间复杂度的较优水平: 2.每道题的Solution先 ...

  3. Entity framework 加载多层相关实体数据

    Entity framework有3种加载数据的方式:懒汉式(Lazy loading),饿汉式(Eager loading),显示加载(Explicit loading).3种加载方式有各自的优缺点 ...

  4. postman(动态数据获取)

    一:返回报文为 json 格式 示例:因为充值记录接口中需要用到登录接口返回报文中的信息如下 1.以获取token(JWT)和uid为例 2.在登录接口的tests中写入代码(因为登录接口报文信息中有 ...

  5. 基于thinkphp3.2.3开发的CMS内容管理系统(二)- Rbac用户权限

    基于thinkphp3.2.3开发的CMS内容管理系统 thinkphp版本:3.2.3 功能: --分类栏目管理 --文章管理 --商品管理 --用户管理 --角色管理 --权限管理 --友情链接管 ...

  6. 支付宝小程序serverless---获取用户信息(头像)并保存到云数据库

    支付宝小程序serverless---获取用户信息(头像)并保存到云数据库 博客说明 文章所涉及的资料来自互联网整理和个人总结,意在于个人学习和经验汇总,如有什么地方侵权,请联系本人删除,谢谢! 我又 ...

  7. Node 内存泄漏排查案例

    背景 在阿里云上看到我运行了一段时间的程序,发现 memory 一项基本是在稳步提升,就知道有内存泄漏的情况出现.如下图 近三日从 35% 升到 40%,缓慢而坚定的提升. 代码 排查此问题需要分析其 ...

  8. IDEA的窗口布局设置

    修改idea的窗口布局 idea默认的窗口模式是如: 可以通过File->Appearance->Window Options->勾选 Widescreen tool window ...

  9. 如何优雅的使用Fegin去构造通用的服务调用的API

    第一步: 创建一个公共的API服务:命名为api(根据自己实际情况进行命名) <?xml version="1.0" encoding="UTF-8"?& ...

  10. 用python爬了厦门人才网的.net岗位

    为了看看.net的就业行情怎么样,用python爬取了厦门人才网.net岗位的信息,话不多说上代码,python没学多久,如果有什么不妥请指正 import requests from bs4 imp ...