一般搜索算法的流程框架
DFS和BFS与一般搜索流程的关系
如果一般搜索算法流程4使用的是stack栈结构(先进后出,后进先出)那么就会越搜越深。即,DFS,DFS只保存当前一条路径,其目的是枚举出所有可能性。反之,如果流程4使用的是queue队列结构(先进先出)那么就会先处理相邻的,然后再进入下一层,即,BFS
递归DFS的流程框架
用栈的DFS和用递归的DFS区别
46. 全排列
思路
DFS + 回溯
code
#include<iostream>using namespace std;const int N = 10;int n = 3;
//最终输出
int path[N];
//记录当前使用过的数
int st[N];void dfs(int u)
{//达到阈值if (u == n){for (int i = 0; i < n; i++)printf("%d ", path[i]);puts("");return;}for (int i = 1; i <= n; i++){if (!st[i]){//存入当前内容path[u] = i;//标记当前数已使用st[i] = true;//进入下一层dfs(u + 1);//dfs结束恢复现场path[u] = 0;st[i] = false;}}
}int main()
{dfs(0);return 0;
}
DFS代码深层分析
1.首先需要两个全局定义,一个用来存储已经使用过的内容st,一个用来存储需要的内容path
2.首选处理第一个需要处理的位置位置u == 0,进入dfs中首先判断是否达到既定目标,u == n,如果没有达到,说明还没有走到尽头,继续执行
3.在进入for循环中后,首先针对u == 0需求位置,寻找合适的内容填入,for第一层循环i = =1开始进行判断,判断i == 1是否使用过,如果没有使用过,就把它加入到需求记录path[u]中,并且标记为i == 1已经使用过,本次需求位置u == 0的位置结束,进入下一个需求位置 u + 1(u == 1)
4.进入新的需求的位置后,依然判断是否达成条件,没达成继续进入for循环,此时全局记录的位置i = =1被使用过,所以来到i = = 2处使用,当本次需求位置处理完毕后 u == 1,继续进入下一个需要处理的位置 u + 1 (u == 2)
5.当i == 3时候所有需求处理完毕,在进入循环(第四次处理的时候)触发了满足条件,直接输出了完整的path[u]内容,然后回到了上次处理,因为for中i = 3了,所以此次处理结束,回溯到上一次,并且重置条件,即 i = 2相关全部清理,然后继续i++ 为3,此时的需求依然是u + 1 = 2的情况,只不过是for为3把i = 3的情况加入到了需求位置,然后进入了下一层u + 1的循环,这样做的目的是for宗到i++保证了是一直向前的状态,再次进入到u + 1中处理的就是最后一个需求位置了,所以当查询到i = 2的时候就执行填入了,满足了所有需求输出。以此类推,输出全部可能性。
class Solution {
public:vector<vector<int>> ans;vector<bool> st;vector<int> path;vector<vector<int>> permute(vector<int>& nums) {for (int i = 0; i < nums.size(); i ++ ) st.push_back(false);dfs(nums, 0);return ans;}//nums为当前处理序列,u为当前要处理的位置void dfs(vector<int> &nums, int u){if (u == nums.size()){ans.push_back(path);return ;}for (int i = 0; i < nums.size(); i ++ )if (!st[i]){st[i] = true;path.push_back(nums[i]);dfs(nums, u + 1);st[i] = false;path.pop_back();}}};
N皇后问题
#include <iostream>using namespace std;const int N = 20;int n;
char g[N][N];
bool col[N], dg[N], udg[N];void dfs(int u)
{//u代表当前处理的行if (u == n){for (int i = 0; i < n; i ++ ) puts(g[i]);puts("");return;}//对每一行的每个列位置进行枚举处理for (int i = 0; i < n; i ++ )if (!col[i] && !dg[u + i] && !udg[n - u + i]){g[u][i] = 'Q';col[i] = dg[u + i] = udg[n - u + i] = true;dfs(u + 1);col[i] = dg[u + i] = udg[n - u + i] = false;g[u][i] = '.';}
}int main()
{cin >> n;for (int i = 0; i < n; i ++ )for (int j = 0; j < n; j ++ )g[i][j] = '.';dfs(0);return 0;
}
104. 二叉树的最大深度
思路
递归求解:
当前树的最大深度等于左右子树的最大深度加1。
时间复杂度分析:树中每个节点只被遍历一次,所以时间复杂度是 O(n)。
code
class Solution {
public:int maxDepth(TreeNode* root) {if(!root)return 0;return max(maxDepth(root->left),maxDepth(root->right)) + 1;}
};
111. 二叉树的最小深度
思路
深度只对叶子节点有效,对内部节点无效。
对于每个节点:
如果树根为空,则返回0。
如果没有子节点,说明是叶节点,则返回1;
如果有子节点,说明是内部结点,则返回子节点的深度的最小值 + 1(加上根节点这层);
时间复杂度分析:每个节点仅被遍历一次,且遍历时所有操作的复杂度是 O(1),所以总时间复杂度是 O(n)。
以本题20为计算节点高度,其左子树高度为1,右子树高度为1,取min 然后加上20这一层,20的高度为2。递推到3的时候 9的高度为1,20的高度为2,min为1,加上1为2。为最终答案
code
class Solution {
public:int minDepth(TreeNode* root) {if (!root) return 0;int res = INT_MAX;if (root->left) res = min(res, minDepth(root->left) + 1);if (root->right) res = min(res, minDepth(root->right) + 1);if (res == INT_MAX) res = 1;return res;}
};
17. 电话号码的字母组合
思路
code
//非递归方式
class Solution {
public:string chars[8] = {"abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};vector<string> letterCombinations(string digits) {//判空if(digits.empty())return vector<string>();vector<string>state(1,"");for(auto u:digits){vector<string> now;//char[u - '2'] 用来确定当前号码是哪个区间取值for(auto c:chars[u - '2'])for(auto s:state)//(s + r)采用字符追加方式now.push_back(s + c);//更新外部存储 state = now; }return state;}
};//递归方式
class Solution {
public:vector<string> ans;string strs[10] = {"", "", "abc", "def","ghi", "jkl", "mno","pqrs", "tuv", "wxyz",};vector<string> letterCombinations(string digits) {if (digits.empty()) return ans;dfs(digits, 0, "");return ans;}void dfs(string& digits, int u, string path) {if (u == digits.size()) ans.push_back(path);else {for (auto c : strs[digits[u] - '0'])dfs(digits, u + 1, path + c);}}
};
94. 二叉树的中序遍历
思路:
因为中序遍历采用【左根右】的形式,从根结点向子树深处搜索,所以使用DFS进行深度优先遍历。
code
class Solution {
public:vector<int>res;vector<int> inorderTraversal(TreeNode* root) {dfs(root); return res;}void dfs(TreeNode* root){if(!root)return;dfs(root->left);res.push_back(root->val);dfs(root->right);}};
200. 岛屿数量
思路:Flood Fill算法
class Solution {
public:vector<vector<char>> g;int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};int numIslands(vector<vector<char>>& grid) {g = grid;int cnt = 0;for (int i = 0; i < g.size(); i ++ )for (int j = 0; j < g[i].size(); j ++ )if (g[i][j] == '1') {dfs(i, j);cnt ++ ;}return cnt;}void dfs(int x, int y) {//染色 g[x][y] = 0;for (int i = 0; i < 4; i ++ ) {int a = x + dx[i], b = y + dy[i];if (a >= 0 && a < g.size() && b >= 0 && b < g[a].size() && g[a][b] == '1')dfs(a, b);}}
};