DFS(深度优先遍历、N皇后问题、2N皇后问题)

这篇具有很好参考价值的文章主要介绍了DFS(深度优先遍历、N皇后问题、2N皇后问题)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

一、回溯法与深度优先搜索(DFS)

二、DFS与二叉树的前序遍历

2.1、二叉树的前序遍历

2.2、DFS

全排列

 2.3、分析

三、N皇后问题


一、回溯法与深度优先搜索(DFS)

1. 回溯法:

  • 是一种通过探索所有可能的候选解来找出所有解的算法。如果候选解被确认不是一个解的话(或者至少不是最后一个解),回溯法会通过在上一步进行一些变化来摆脱当前不正确的解,重新尝试其他的可能性。
  • 它通常用于解决决策问题,如排列、组合、子集等。
  • 回溯法可以隐式地处理图或树,即这些结构并不需要事先构建出来,而是在搜索过程中动态生成。

2. 深度优先搜索(DFS):

  • 是一种用于遍历或搜索树或图的算法。在树中,这种算法搜索最深的节点,而在图中,它将回溯到未探索过的路径。
  • DFS从根(或在图中的某个任意节点)开始,探索尽可能深的分支,直到达到目标节点,或者当前分支没有更多的节点可以访问。然后,搜索回溯到开始探索的路径上的下一个节点。
  • DFS通常使用栈或递归来实现,其中递归实现更为常见和直观。

关系:

  • 回溯法通常使用DFS作为其基本的搜索策略。在回溯法中,DFS用于系统地遍历所有可能的解空间。
  • 当我们说“一条路走到黑”时,我们实际上是在描述DFS的特性,即尽可能深入地搜索图的分支,直到达到叶节点或无法继续为止。

DFS(深度优先遍历、N皇后问题、2N皇后问题),深度优先,算法,c++,DFS,数据结构

  • 在排列型问题中,DFS用于生成所有可能的排列,而在子集型问题中,它用于生成所有可能的子集。

尽管在很多情况下回溯法和DFS是紧密相关的,但它们并不总是等价的。回溯法更侧重于问题的求解策略,而DFS更侧重于图的遍历策略。然而,在实际应用中,这两个概念经常交织在一起。

二、DFS与二叉树的前序遍历

2.1、二叉树的前序遍历

DFS(深度优先遍历、N皇后问题、2N皇后问题),深度优先,算法,c++,DFS,数据结构

前序遍历的步骤如下:

DFS(深度优先遍历、N皇后问题、2N皇后问题),深度优先,算法,c++,DFS,数据结构

// 先序遍历二叉树  
void PrevOrder(BTNode* root)
{
	// 如果当前节点为空,则打印"NULL"并返回  
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	// 访问当前节点的数据  
	printf("%c ", root->data);
	// 递归遍历左子树  
	PrevOrder(root->left);
	// 递归遍历右子树  
	PrevOrder(root->right);
}

2.2、DFS

基本模型:

void dfs(int step){
	判断边界
	枚举每一种可能 for(i=1;i<=n;i++){
		继续下一步 dfs(step+1)
		}
}

全排列

题目:

输入一个数n,将数字1~n排成一排,按字典序输出所有排列方法。

#include<bits/stdc++.h>
using namespace std;
const int N = 10;
int ans[N]; 
bool mark[N];// 标记是否走过
int n;

void dfs(int u) // u代表目前dfs深度
{
	if (u == n)// 判断边界, 找到解
	{
		for (int i = 0; i < n; i++)
			cout << ans[i] << " ";
			// 打印当前排列
		cout << '\n';
		return;
	}
	for (int i = 1; i <= n; i++)// 枚举下一种情况
	{
		if (mark[i] == false)
		// 尝试将每个未使用的数字放入当前位置
		{
			mark[i] = true;// 标记已用
			ans[u] = i;// 数字i放入当前深度u的位置
			dfs(u + 1);// 递归调用dfs函数,处理下一个深度
			mark[i] = false;
			ans[u] = 0;
		}
	}
}

int main()
{
	cin >> n;dfs(0);//从0开始调用dfs函数生成排列
	return 0;
}

这是一个排列型搜索树,实际上的回溯法比较灵活,需要根据题意要求来具体分析。vis[i]表示数字i是否使用过,也经常被用于表示某个元素是否使用过al]存放结果,当dep深度=n+1时说明n层都已经算完了,直接输出结果。子集型搜索树模板结构类似,就是在往下走的时候只有两条边,表示“选或不选当前这个元素”

 2.3、分析

二叉树的前序遍历确实与深度优先遍历(DFS)在原理上是相似的。前序遍历是二叉树深度优先遍历的一种形式。

  • 前序遍历顺序:在二叉树的前序遍历中,我们首先访问当前节点(根节点或任意子树的根),然后递归地前序遍历左子树,最后递归地前序遍历右子树。这个“根-左-右”的顺序确保了遍历是深度优先的。
  • 深度优先遍历:深度优先遍历是一种树或图遍历算法,它从根节点(或任意节点)开始,尽可能深地探索图的分支。在树中,这意味着沿着树的最深路径进行搜索,直到到达叶节点或无法再深入,然后回溯到开始搜索的路径上的下一个节点。

在二叉树的前序遍历中,每个节点被访问的顺序实际上反映了DFS搜索树的方式。先访问当前节点对应于DFS中的“探索当前节点”,然后深入左子树对应于“先探索最左边的分支”,最后访问右子树则是“在左侧无更多可探索路径时,回溯并探索右侧的分支”。

因此,我们可以说,二叉树的前序遍历是一种特殊形式的深度优先遍历,其中特定的节点访问顺序(根-左-右)体现了DFS的基本原则。两者都是基于深度优先搜索的概念来遍历结构的。

三、N皇后问题

题目描述:

在一个 N x N 的方格棋盘上放置 N 个皇后,要求它们互不攻击,即任意两个皇后不允许处在同一行、同一列,也不允许处在与棋盘边框成 45 度角的斜线上。给定一个正整数 N(N < 10),你的任务是求出在这样的棋盘上放置 N 个皇后的合法方法有多少种。

输入描述:

输入包含一个正整数 N(N <= 10),表示棋盘的大小和需要放置的皇后的数量。

输出描述:

输出一个正整数,表示在给定大小的棋盘上放置 N 个皇后的合法方法数量。

输入:5     

输出:10

思路:对于这种题,首先,我们想到的是使用二维数组存,然后暴力枚举,判断函数来一个一个判断。那么,就得到了一个大概的思路:对二维数组的所有情况进行枚举,然后对每种情况进行判断,这是这种题目的普遍思想,接下来是对题目进行细致的分析。

这种题主要的难点是判断、遍历如何实现。由题意可知,一行,一列中最多有一个皇后存在,所以可以把一行或一列看成一组,这里我们把一行看成一组。因为第一行是没有放过任何皇后的,所以第一行全部都枚举放置皇后,接下来的每行,我们可以设置一个check函数来检查是否可以放置皇后,这时,就构成了我们代码的完整思路。

DFS(深度优先遍历、N皇后问题、2N皇后问题),深度优先,算法,c++,DFS,数据结构

代码如下:

#include<bits/stdc++.h>
using namespace std;
int a[11][11]; 
// 存储棋盘的数组,1表示为皇后,0表示为空 
    int ans = 0; int n;
// 解的数量, 棋盘的大小,即N

// 判断是否有攻击
bool check(int deep, int m) {
    for (int k = 0; k < n; ++k) {
        if (a[k][m]) return false;
        // 检查第 m 列是否有皇后
    }
    // 检查所有方向以判断皇后是否会攻击
    //下方还没有放置皇后,所以不用检查
    for (int i = 1; i <= deep; i++) {
        if (a[deep - i][m]) return false; // 检查上方
        if (m - i >= 0 && a[deep - i][m - i]) return false; // 检查左上方
        if (m + i < n && a[deep - i][m + i]) return false; // 检查右上方
    }
    return true;
}
void dfs(int deep) {
    if (deep == n) {
        ans++;// 找到一个解,解的数量加一
        return;
    }
    for (int i = 0; i < n; ++i) {
    // 尝试在当前行的每一列放置皇后
        if (check(deep, i)) {
            a[deep][i] = 1; // 放置皇后
            dfs(deep + 1);// 递归搜索下一行
            a[deep][i] = 0; // 回溯,移除皇后
        }
    }
}
int main() {
    cin >> n;dfs(0);// 从第0行开始深度优先搜索
    cout << ans;
    return 0;
}

四、2N皇后问题

问题描述

  给定一个n*n的棋盘,棋盘中有一些位置不能放皇后。现在要向棋盘中放入n个黑皇后和n个白皇后,使任意的两个黑皇后都不在同一行、同一列或同一条对角线上,任意的两个白皇后都不在同一行、同一列或同一条对角线上。问总共有多少种放法?n小于等于8。

输入格式

  输入的第一行为一个整数n,表示棋盘的大小。
  接下来n行,每行n个0或1的整数,如果一个整数为1,表示对应的位置可以放皇后,如果一个整数为0,表示对应的位置不可以放皇后。

输出格式

  输出一个整数,表示总共有多少种放法。

样例输入

4
1 1 1 1
1 1 1 1
1 1 1 1
1 1 1 1

样例输出

2

样例输入

4
1 0 1 1
1 1 1 1
1 1 1 1
1 1 1 1

样例输出

0

此题为N皇后问题的进阶版,相较于N皇后问题需多考虑一组棋子,并且要考虑是否能放入。我们可以先放入黑皇后,再放入白皇后的思路去实现代码。此时要建立dfsblack和dfswhite两个函数。

#include<bits/stdc++.h>
using namespace std;
int a[10][10],
b[10][10],// 存储黑皇后的位置
c[10][10];// 存储白皇后的位置
bool vis[10][10];
int n;// 棋盘大小
int ans;// 几种方法
// 判断是否有攻击
bool checkblack(int deep, int m) {
    // 检查所有方向以判断皇后是否会攻击
    //下方还没有放置皇后,所以不用检查
    for (int i = 0; i <= deep; i++) {
        if (b[deep - i][m]) return false; // 检查上方
        if (m - i >= 0 && b[deep - i][m - i]) return false; // 检查左上方
        if (m + i < n && b[deep - i][m + i]) return false; // 检查右上方
    }
    return true;
}
bool checkwhite(int deep, int m) {
    // 检查所有方向以判断皇后是否会攻击
    //下方还没有放置皇后,所以不用检查
    for (int i = 0; i <= deep; i++) {
        if (c[deep - i][m]) return false; // 检查上方
        if (m - i >= 0 && c[deep - i][m - i]) return false; // 检查左上方
        if (m + i < n && c[deep - i][m + i]) return false; // 检查右上方
    }
    return true;
}

dfsblack

放完黑皇后的终止条件为:

if (deep == n) {
        dfswhite(0);// 找到一个解,解的数量加一
        return;
    }

然后判断的条件也增加为

if (checkblack(deep, i) && a[deep][i] != 0) (0不能放置)

void dfsblack(int deep) {
    if (deep == n) {
        dfswhite(0);// 找到一个解,解的数量加一
        return;
    }
    for (int i = 0; i < n; ++i) {
        // 尝试在当前行的每一列放置皇后
        if (checkblack(deep, i) && a[deep][i] != 0) {
            b[deep][i] = 1; // 放置皇后
            vis[deep][i] = true;
            dfsblack(deep + 1);// 递归搜索下一行
            b[deep][i] = 0; // 回溯,移除皇后
            vis[deep][i] = false;
        }
    }
}

dfswhite

检查白皇后是否能放的条件为:

checkwhite(deep, i) && a[deep][i] != 0 && b[deep][i] == 0 (0不能放置,黑放过不能放)

void dfswhite(int deep) {
    if (deep == n) {
        ans++;// 找到一个解,解的数量加一
        return;
    }
    
    for (int i = 0; i < n; ++i) {
        // 尝试在当前行的每一列放置皇后
        if (checkwhite(deep, i) && a[deep][i] != 0 && b[deep][i] == 0) {
            c[deep][i] = 1; // 放置皇后
            vis[deep][i] = true;
            dfswhite(deep + 1);// 递归搜索下一行
            c[deep][i] = 0; // 回溯,移除皇后
            vis[deep][i] = false;
        }
    }
}
int main() {
    cin >> n; 
    memset(vis, false, sizeof(vis));
    memset(c, 0, sizeof(c));
    memset(b, 0, sizeof(b));
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < n; j++)
            cin >> a[i][j];
    }
    dfsblack(0);// 从第0行开始深度优先搜索
    cout << ans;
    return 0;
}

今天就先到这了!!!

DFS(深度优先遍历、N皇后问题、2N皇后问题),深度优先,算法,c++,DFS,数据结构

看到这里了还不给博主扣个:
⛳️ 点赞☀️收藏 ⭐️ 关注!

你们的点赞就是博主更新最大的动力!
有问题可以评论或者私信呢秒回哦。文章来源地址https://www.toymoban.com/news/detail-841979.html

到了这里,关于DFS(深度优先遍历、N皇后问题、2N皇后问题)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【数据结构】图的创建和深度(DFS)广度(BFS)优先遍历

    图是由顶点的有穷非空集合和顶点之间边的集合组成,通过表示为G(V,E),其中,G标示一个图, V是图G中 顶点的集合 , E是图G中 边的集合 。 图分为 无向图 和 有向图 无向图: 若顶点Vi到Vj之间的边没有方向,则称这条边为无向边(Edge),用序偶对(Vi,Vj)表示。 有向图: 若从

    2024年02月05日
    浏览(40)
  • 数据结构实验6 :图的存储与遍历(邻接矩阵的深度优先遍历DFS和邻接表的广度优先遍历BFS)

    利用邻接矩阵存储无向图,并从0号顶点开始进行深度优先遍历。 输入第一行是两个整数n1 n2,其中n1表示顶点数(则顶点编号为0至n1-1),n2表示图中的边数。 之后有n2行输入,每行输入表示一条边,格式是“顶点1 顶点2”,把边插入图中。 例如: 4 4 0 1 1 3 0 3 0 2 先输出存储

    2024年02月09日
    浏览(47)
  • 图的遍历(搜索)算法(深度优先算法DFS和广度优先算法BFS)

    从图的某个顶点出发访问遍图中所有顶点,且每个顶点仅被访问一次。(连通图与非连通图) 1、访问指定的起始顶点; 2、若当前访问的顶点的邻接顶点有未被访问的,则任选一个访问之;反之,退回到最近访问过的顶点;直到与起始顶点相通的全部顶点都访问完毕; 3、若

    2024年01月17日
    浏览(33)
  • 基于邻接矩阵的有向图的广度优先遍历(BFS)和深度优先遍历(DFS)算法

    概念: 广度优先遍历算法是图的另一种基本遍历算法,其基本思想是尽最大程度辐射能够覆盖的节点,并对其进行访问。 以迷宫为例,广度优先搜索则可以想象成一组人一起朝不同的方向走迷宫,当出现新的未走过的路的时候,可以理解成一个人有分身术,继续从不同的方

    2024年02月06日
    浏览(45)
  • 【数据结构与算法】搜索算法(深度优先搜索 DFS和广度优先搜索 BFS)以及典型算法例题

    【数据结构与算法】系列文章链接: 【数据结构与算法】递推法和递归法解题(递归递推算法典型例题) 【数据结构与算法】系列文章链接: 【数据结构与算法】C++的STL模板(迭代器iterator、容器vector、队列queue、集合set、映射map)以及算法例题 【数据结构与算法】系列文章链

    2024年04月13日
    浏览(33)
  • 【数据结构与算法】图的深度优先和广度优先遍历

    😊😊作者简介😊😊 : 大家好,我是南瓜籽,一个在校大二学生,我将会持续分享Java相关知识。 🎉🎉个人主页🎉🎉 : 南瓜籽的主页 ✨✨座右铭✨✨ : 坚持到底,决不放弃,是成功的保证,只要你不放弃,你就有机会,只要放弃的人,他肯定是不会成功的人。 图是一

    2024年02月02日
    浏览(45)
  • 【数据结构与算法】图的基本概念 | 邻接矩阵和邻接表 | 广度优先遍历和深度优先遍历

    🌠 作者:@ 阿亮joy. 🎆 专栏:《数据结构与算法要啸着学》 🎇 座右铭:每个优秀的人都有一段沉默的时光,那段时光是付出了很多努力却得不到结果的日子,我们把它叫做扎根 图是由顶点集合及顶点间的关系组成的一种数据结构:G = (V, E) ,其中: 顶点集合V = {x|x属于某

    2024年02月04日
    浏览(56)
  • 数据结构学习笔记——图的遍历算法(深度优先搜索和广度优先搜索)

    图的遍历指从图中某一顶点出发(任意一个顶点都可以作为访问的起始顶点),按照某种遍历方法,对图中所有的顶点访问一次且只访问一次。图与树不一样,其中一个顶点可能与多个顶点相连,所以需记录已访问过的顶点,当访问一个顶点后,考虑如何选取下一个要访问的

    2024年02月05日
    浏览(37)
  • 图的遍历(广度优先遍历BFS,深度优先遍历DFS)

    目录 图的遍历概念: 图的广度优先遍历(BFS): 代码实现如下: 测试如下: 注意: 图的深度优先遍历(DFS): 代码实现如下: 测试如下: 总代码: 结语: 给定一个图G和其中任意一个顶点v0,从v0出发,沿着图中各边访问图中的所有顶点,且每个顶点仅被遍历一次。\\\"遍

    2024年02月21日
    浏览(38)
  • 深度优先遍历(DFS)

    图的深度优先遍历类似于树的先根遍历(递归)。 树的先根遍历: 图的深度优先遍历 使用的逻辑结构是栈。 如果是非连通图,依旧无法遍历完所有结点。 所以,需要在遍历完一轮结点后,扫描未被标记的结点。 visited数组防止重复访问。 代码实现: 1.空间复杂度 来自函数

    2024年02月10日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包