前言
最近总感觉没怎么好好敲代码,没刷题,没看书,那就先整理一下以前的知识,正好最近上机要求写马踏棋盘,就借着例题好好分析一下
什么是DFS
深度优先搜索算法(英语:Depth-First-Search,简称DFS)是一种用于遍历或搜索树或图的算法。 沿着树的深度遍历树的节点,尽可能深的搜索树的分支。当节点v的所在边都己被探寻过或者在搜寻时结点不满足条件,搜索将回溯到发现节点v的那条边的起始节点。整个进程反复进行直到所有节点都被访问为止。属于盲目搜索,最糟糕的情况算法时间复杂度为O(!n)。
(from Wikipedia)
DFS是一种盲目的搜索,先说说搜索吧。
搜索就是利用计算机的高性能来有目的不断的穷尽一个问题的部分可能或者全部可能,从而解决问题。
简单的模板
bool check() {
if(满足条件) {
return true;
}
return false;
}
void dfs(int step) {
判断边界条件(找到||找完) {
结束
}
{
判断所有的可能性
满足check的条件
标记
继续进行下一步(step+1)
去掉标记(即回溯)
}
}
刚开始看模板感觉特别的茫然,标记是啥,回溯又是什么呢。
还是通过一个例子来讲吧.
简单例子
一个很简单的问题,但又特别经典。
将马随机放在国际象棋的Board[0~7][0~7]的某个方格中,马按走棋规则进行移动。,走遍棋盘上全部64个方格。编制非递归程序,求出马的行走路线,并按求出的行走路线,将数字1,2,…,64依次填入一个8×8的方阵,输出之。
还是先分析一下吧。
先需要一个棋盘,用一个二维数组来表示一个棋盘
const int size = 8;
int board[size][size];
接下来就是马的行走问题了,马在国际象棋中,按照日字进行移动,我们可以用一个方向数组来表示马的行走方式。假设按图中的方向来走。
int dx[] = {1,2,2,1,-1,-2,-2,-1};
int dy[] = {-2,-1,1,2,2,1,-1,-2};
剩下的就应该是如何地走遍棋盘了^_^
先慢慢地写
void dfs(int x,int y,int cnt) {
}
三个参数的含义:
x表示马坐在的位置的横坐标
y表示马坐在的位置的纵坐标
cnt表示马走了多少步
接着向下写
void dfs(int x,int y,int cnt) {
if(cnt == size * size) {
//进行下一步操作
}
}
当走的步数和棋盘所能布的棋子书相同时,就进行下一步操作
void dfs(int x,int y,int cnt) {
if(cnt == size * size) {
//进行下一步操作
}
for(int i = 0;i < 8;i++) {
int t_x = x + dx[i];
int t_y = y + dy[i];
//尝试把马的八个行走方向全部尝试
//t_x,t_y表示马走之后的新的位置坐标
if(check(t_y,t_y)) {
//进行下一步操作
}
}
}
写到这里,到了check()函数,这里check()函数的意义就是检测马的下一步能不能走
限制因素:
- 棋盘边界
- 马走过一次的位置不能够重复再走
那就开始写check()函数
int check(int x,int y) {
if(x < 0 || x >= size || y < 0 || y >= size || board[x][y]) {
//当board[x][y]马走过的话,我们就把它的值修改为当前的步数,所以不为0就代表了马走过了这个地方
return 0;
}
return 1;
}
check()写完了,然后按照模板就继续写dfs了
void dfs(int x,int y,int cnt) {
if(cnt == size * size) {
//进行下一步操作
}
for(int i = 0;i < 8;i++) {
int t_x = x + dx[i];
int t_y = y + dy[i];
if(check(t_x,t_y)) {
board[t_x][t_y] = cnt;
//这就相当于了标记,把已经走过的棋子标记,防止死循环
dfs(t_x,t_y,cnt+1);
//我们继续对下一个位置进行搜索,利用递归,马从t_x,t_y开始搜索,步数同时加1
board[t_x][t_y] = 0;
//取消标记,当这一步之后马没有位置可走,那么就需要撤回这步
}
}
}
整体的框架基本出来了^_^
DFS就是一种不撞南墙不回头,全部走完才放心
最终代码的整理
#include <stdio.h>
#include <stdlib.h>
const int size = 8; //规定棋盘的大小
int board[size][size];
int dx[] = {1,2,2,1,-1,-2,-2,-1};
int dy[] = {-2,-1,1,2,2,1,-1,-2};
//方向数组
int check(int x,int y) {
if(x < 0 || x >= size || y < 0 || y >= size || board[x][y]) {
return 0;
}
return 1;
}
void dfs(int x,int y,int cnt) {
if(cnt == size * size) {
//进行下一步操作
for(int i = 0;i < size;i++) {
for(int j = 0;j < size;j++) {
printf("%2d ",board[i][j]);
}
printf("\n");
}
printf("\n");
exit(0);//这里结束进程,只输出一个解
}
for(int i = 0;i < 8;i++) {
int t_x = x + dx[i];
int t_y = y + dy[i];
if(check(t_x,t_y)) {
board[t_x][t_y] = cnt;
//这就相当于了标记,把已经走过的棋子标记,防止死循环
dfs(t_x,t_y,cnt+1);
//我们继续对下一个位置进行搜索,利用递归,马从t_x,t_y开始搜索,步数同时加一
board[t_x][t_y] = 0;
}
}
}
int main() {
dfs(0,0,1);
return 0;
}
解决这个问题,大家是不是对于DFS有了一点了解,解决了基本的马踏棋盘问题^_^
其实DFS能解决很多的问题,比如迷宫问题,
联通块,类似皇后问题等等,应用也是很广泛。希望大家看完能有所收获。