IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> Acm入门5:深度优先搜索(第六篇博客) -> 正文阅读

[数据结构与算法]Acm入门5:深度优先搜索(第六篇博客)

·搜索,是通过穷举一个问题的所有或部分可能的情况来确定问题的解。

·穷举,奠定了搜索,特别是深度优先搜索的基调,使它成为了—种复杂度通常较大的算法,例如:

·求全排列,复杂度为O(n!)

·求从一个数列选择任意个数的所有情况,复杂度为0(2")

全排列:

全排列就是一个数列的所有可能的排列情况。例如对于数列[i,2,3],它的全排列为:

[1,2,3],[1,3.2],[2,1,3],[2,3,1],[3,1,2],[3, 2,1]我们如何做到对所有情况不重不漏的枚举呢?
想要不重不漏,我们需要制定枚举的规则:对于三个空位_? _? _我们从左至右依次填数,对于每一个位置,优先枚举填入1的情况,其次是2和3,当这个数字被用过时跳过这个数。

用递推循环表示这个过程:

for (int i = 1; i <= 3; i++) i
vis[i] = 1;
a[1] = i; ///填入第一个数,标记已经使用过
for(int j = 1; j <= 3; j) i
if (vis[j])continue; // 如果已经用过则跳过
vis[j] = 1;
a[2] = j; illl11ll11 // 填入第二个数,标记已经使用过
for(int k = 1; k <= 3; k++) {
	if (vis[k])continue; // 如果使用过则跳过
a[3] = k;
	print(a); // 所有数已经填完,输出
		vis[j] - 0; //这一次枚举结束,将标记去除
}
vis[i] = e; //同上  重点!!!
)

·在只有三个数时,递推固然简单,可当数字越多,需要写的循环也变得越来越多,递推的写法显然不够优雅。
·观察可以发现,每次填数的过程是一模一样的。那我们面对完全一样的算法过程,用什么方法可以让我们的代码变得简洁呢?
·函数。
·我们可以利用函数不断调用自己来实现这个重复的过程。·这就是递归。
?

void dfs(int now) {// now即当前要填的是第几个数
if (now == 4){ // 递归边界
print(a);
return;
//当所有数都填完后输出
for (int i = 1; i <= 3; i++) {//寻找一个没被使用过的数字
	if (vis[i])continue;
	vis[i] = 1;
	a[now] = i; //同递推过程
	dfs(now + 1); // 相当于递推过程的下一层循环
	vis[i] = 0; // 回溯
}
return;
}

边界和回溯

为什么要有递归边界
递归边界为递归过程设置了一个终止条件,这个终止条件一般是一种情况的答案已经求得,也有可能是这种情况不可能成为最终答案(这就是之后要讲的剪枝)。
例如求全排列时的递归边界就是所有数都填完。而没有递归边界的递归就像一个无数层嵌套的循环。

·回溯法就像递推的循环结束时将标记清除的过程。
·当dfs(now +1)的套娃到达递归边界返回时,注意这里返回的是上层函数(即前一个位置),并在这里执行visi]=o将标记清除。
?

void dfs(int now) {
	// now即当前要填的是第几个数
		if (now == 4) //递归边界
			print(a);
	return;
}// 当所有数都填完后输出
for (int i = 1; i <= 3; i++)//寻找一个没被使用过的数字
if (vis[i])continue;
vis[i] = 1;
a[now] = i; // 同递推过程
dfs(now + 1); // 相当于递推过程的下一层循环
vis[i] = 0; // 回溯
}
return;

?上面递归求全排列的过程其实就是深度优先搜索。?
dfs的基本框架如下:


void dfs(...) i //递归参数:当前故举到哪一步
if (到达终点) {
	记录答案
		return;
}
if (当前状态不合法)return;
if (达到剪枝条件).return这两行是等效的,只用存在其一for(...) i //枚举状态
if (将要访问的状态不合法)continue; 操作
标记已经访问
dfs(下一步);
(还原标记)回溯法
}
}

例题:八皇后问题

在一个8x8的国际象棋棋盘上摆放8个皇后,使它们互相不能吃掉对方,即这8个皇后互相不同行不同列,也不在同一条对角线的平行线上。
要求按字典序输出所有可能的摆放情况,每种情况一行,按行号从小到大的顺序输出每个皇后的列号。
首先要确定枚举规则。题目要求按行号从小到大的顺序输出,就告诉了我们要按每一行来进行枚举。
递归参数:行号。
递归边界:所有边都枚举完成。要枚举的状态:每一列。
不合法状态:当前位置的行或列或对角线的平行线上有其他皇后。思考如何标记,如何判断当前列可不可用。
注意回溯。


明天继续写

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-01-29 23:20:21  更:2022-01-29 23:21:20 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 17:31:06-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码