DFS深搜与BFS广搜专题

一般搜索算法的流程框架

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

༄yi笑奈何

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值