宽度优先搜索BFS 和深度优先搜索DFS的比较。。

题目链接:https://vijos.org/p/1016

首先简单介绍下题目。就是有9个挂钟,时间只存在3,6,9,12 这4种状态对应的 状态编号是 1,2,3,0,然后给你9种操作时钟的方式,每次可以事操作的时钟状态编号+1,如果编号到了4,就是表示为0。目标就是把9个闹钟全部变为0状态。题目大致是这个意思,不懂的可以看看上面的题目链接。

首先我从我比较熟悉的DFS讲起,这是深度优先搜索,它的优点就是如果目标答案需要很多步骤完成,这样有几率一次就完成了,不过缺点在于需要每次都是把所有操作数都枚举一遍(0~4)之间,0表示不使用当前操作,但是你还是得在代码里面体现。

 DFS好理解,但是所谓的不必要的堆栈开销,严重影响计算机性能,可能你对于简单的操作步骤,和复杂的操作步骤,搜索时间可能相差不大。

而BFS相当于,如果步骤简单可以很快得出结果,步骤复杂就是很慢。这样就对应了需要计算的规模,但是编程复杂度还是稍高一些,也可能不太理解。

下面我写两个demo

DFS 这边可以不保存每次的状态信息。也就是状态信息可以不用数组存储起来。有两种方法处理,不同的状态1.你可以用公用的全局变量保存,但是需要回溯,这个优先,不会错。2,你可以使用boolean dfs() 用return的方法返回当前结果的值,这个不需要回溯,但是使用boolean 来判断搜索是否正确。也可以使用返回值,这个也可以但是个人感觉不好把握。回头研究下有返回值的DFS

public void dfs(int d){

int (d==k)//如果搜索深度到了指定的k、直接优先返回,我觉得这样写最标准

{

if(sum == target){ //这里需要判断是否搜索到了结果 如果搜索到了 加一个全局flag标志

flag = true;

return ;

}

return;//不管搜索到 都是返回

}

for(int i =0;i<=3;i++)//注意这边需要带0 表示当前节点不使用。

{

sum+ = i*mm[d];//mm[d]表示当前深度的搜索节点值,可以搜0到3次,0次代表不搜索,结果放sum里面

save[d] = i;//这里用全局save 保存当前节点值的次数。保存结果而已

dfs(d+1);//搜索下一层

sum-= i*mm[d];//这边注意回溯下

}

if(flag)//这里一次搜索结束判断是否 全局flag为true

{

return ; //搜索到结果可以直接ruturn 或者你可以等全部搜索完成,记录下最小值。这边可以自己发挥。每次搜索到结果 都和min比较下,如果小就留下,并且更新min的值 和全局保存结果的数组

}

}

//基本上我认为的DFS写完了,可能有不对的地方。。。

BFS的demo我不太熟练,因为我不经常用BFS,但是可以大概描述下 https://blog.csdn.net/jiange702/article/details/81365005 这篇文章介绍的很不错

bfs就是利用队列形式实现,相当于收工模拟,他就是需要每次都要保存状态信息,因为两个搜索之间事没有联系的。只有搜索到了最后结果,才会把当前记录的拿出来。而且不依赖全局变量存储,只需要从自己的变量里面取就好了。所以对于BFS我们在c语言中需要定义结构体,而不是全局边量。但是java中没有结构体,我们可以利用map存一下吧。因为队列里的搜索每一个都是独立的个体,所以不能使用全局变量。我们可以用Hashmap里面保存一下几个值{当前状态信息 value可能是数组list  也可以使用二进制表示状态,搜索了几步 这个可以用int,每一步搜索的具体形式(相当于每一步都搜索了哪些值)这个用数组} ,每次我们都拿当前状态信息比较目标状态,如果匹配,就可以输出答案,并且结束循环。当然你也可以先结束循环,比如先拿数组第一个元素,比较是否是目标信息,不是就出队列。如果是,就return; 然后输出数组第一个元素的相关信息。

优点就是根据数据规模来运行时间,但是保存的东西太多了。。。。可能会出错。而且内存也占用太大。 当然肯定有优化,也可以使用全局来优化,map里面可以只保存 步骤和 每一步怎么操作的。然后根据这些信息算出状态。。还不如每次保存呢。。。。。。

下面给出demo

public void BFS(){

ArrayList my = Arrays.copy(m);//这边m表示要操作的初始状态数组,首先复制一下,不然原始数据没了

Map first = new HashMap(4);//这边需要先第一个节点入队列 这个节点保存初始信息

first.add("now_status",my);//这边是保存初始状态

first.add("pos",0);//这边保存多少搜索多少步骤,如果不需要输出可以不要

first.add("operate",new ArrayList(pos));//这个数组是操作数组,代表每步搜索了啥,初始肯定是空

queue.push(first);//这边自己定义个队列,然后入队列

visit[my] = true;

pos=0;//全局操作步骤

while(!queue.isEmpty()){//队列不为空,如果为空表示搜索完了还没结果

Map a = queue.pop();//取第一个

if(a.get("now_status") == target_status) 

{

  print;//这边输出结果 通过 a.get("operate").get(pos);输出拉 ,步骤肯定是最小的

   return ;

}

pos++;

for(int i =1; i<=9; i++)

{

status = a.get("now_status")+= mm[i];//mm[i]表示每个操作 

if(!visit[status])//新的状态加入队列 老状态不加

{

//构造map

map.put("pos",pos); 

//保存操作的mm[i]

map.put("operate","")

//存入新的status

map.put("now_status",status );

queue.push(map);

}

}

//结束

}

}

差不多BFS没了 下面给出我认为比较好的BFS和DFS算法 复制的哦

DFS

#include
#include
#include
#include
#include
using namespace std;
 
 
bool flag[4][4][4][4][4][4][4][4][4];
int op[10][10] = {{0},{1,2,4,5},{1,2,3},{2,3,5,6},{1,4,7},{2,4,5,6,8},
                  {3,6,9},{4,5,7,8},{7,8,9},{5,6,8,9}};
 
int st[10];
struct node
{
    int state[10];
    int pos;//从起始状态到当前状态经过的操作数
    int ans[200];//从起始状态到当前状态所有的操作
    friend bool operator < (struct node a,struct node b)
    {
        return a.pos > b.pos;
    }
}s,now;
 
void Bfs()
{
    priority_queue lcm;
    int i,j;
    memset(flag,0,sizeof(flag));
    for(i = 1;i <= 9;i ++)
        s.state[i] = st[i];
    s.pos = 0;
    flag[st[1]][st[2]][st[3]][st[4]][st[5]][st[6]][st[7]][st[8]][st[9]] = 1;
    lcm.push(s);
    while(!lcm.empty())
    {
        now = lcm.top();
        lcm.pop();
        for(i = 1;i <= 9;i ++)
        {
            s = now;
            for(j = 0;op[i][j];j ++)
            {
                s.state[op[i][j]] ++;
                if(s.state[op[i][j]] >= 4)
                    s.state[op[i][j]] -= 4;
//                s.state[op[i][j]] %= 4;
            }
 
 
            if(!flag[s.state[1]][s.state[2]][s.state[3]][s.state[4]][s.state[5]][s.state[6]][s.state[7]][s.state[8]][s.state[9]])
            {
                s.ans[s.pos ++] = i;
                flag[s.state[1]][s.state[2]][s.state[3]][s.state[4]][s.state[5]][s.state[6]][s.state[7]][s.state[8]][s.state[9]] = 1;
                lcm.push(s);
            }
            if(!s.state[1] && !s.state[2] && !s.state[3] && !s.state[4] && !s.state[5]
               && !s.state[6] && !s.state[7] && !s.state[8] && !s.state[9])
            {
             //   printf("%d",s.ans[0]);
                sort(s.ans,s.ans + s.pos);//所有的操作互不干扰,无先后之分的
                for(j = 0;j < s.pos;j ++)
                    printf("%d ",s.ans[j]);
                printf("\n");
                return;
            }
 
 
        }
    }
}
 
int main()
{
    int i,j;
    while(scanf("%d",&j) != EOF)
    {
        st[1] = j;
        for(i = 2;i <= 9;i ++)
        {
            scanf("%d",&st[i]);
        }
        Bfs();
    }
    return 0;
}
下面是我自己写的dfs 感觉思路很清晰 可以给各位大佬参考下

import java.util.Scanner;

public class Main {

	 int[][] target = {{0},{1,2,4,5,0},{1,2,3,0},{2,3,5,6,0},{1,4,7,0},{2,4,5,6,8,0},{3,6,9,0},{4,5,7,8,0},{7,8,9,0},{5,6,8,9,0}};
	 int[] output   = new int[10];
	 int[] record   = new int[10];
	 boolean flag = false;
	 public void dfs(int deep) {
		 if(deep == 10) {
			flag = check();
			return ;
		 }

		 for(int i =0; i<=3; i++) {
			 change(deep,i);
			 record[deep] = i ;
			 dfs(deep+1);
			 if(!flag) {
			 change(deep ,-i);
			 }
			 else {
				 return;
			 }
		 }
	 }
	 public boolean check() {
		 for(int i =1; i<=9; i++) {
			 if(output[i]!=0) {
				 return false;
			 }
		 }
		 return true;
	 }
	 public void change(int deep, int i ) {
		 if(i !=0) {
			int j=0;
		 while(target[deep][j]!=0)
		 {
			 output[target[deep][j]] +=4;//不够减
			 output[target[deep][j]] += i;
			 output[target[deep][j]] %= 4;
			 j++;
		 }
			
		 }
	 }
	 
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int i=1;
		Main problem = new Main();
		while(i<=9) {
			problem.output[i] = sc.nextInt();
			problem.record[i] = 0;
			i++;
		}
	    problem.dfs(1);
	   
	    for(i=1; i<=9; i++) {
	    	while(problem.record[i]>0) {
	    		System.out.print(i+" ");
	    		problem.record[i]--;
	    	}
	    }
	}
	
}

BFS

#include
using namespace std ;
//Vijos P1016

const int way[9][9] = {{1 , 1 , 0 , 1 , 1 , 0 , 0 , 0 , 0} , {1 , 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0} , {0 , 1 , 1 , 0 , 1 , 1 , 0 , 0 , 0} , {1 , 0 , 0 , 1 , 0 , 0 , 1 , 0 , 0} , {0 , 1 , 0 , 1 , 1 , 1 , 0 , 1 , 0} , {0 , 0 , 1 , 0 , 0 , 1 , 0 , 0 , 1} , {0 , 0 , 0 , 1 , 1 , 0 , 1 , 1 , 0} , {0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 1} , {0 , 0 , 0 , 0 , 1 ,1 , 0 , 1 , 1}} ;//9种方式改变不同的钟,1代表加3点,0代表不变
bool judge ;//判断是否调整完成
int x[9] , ans[9] ;
void dfs(int num)
{
    if (num == 9)//9种方式全部搜完
    {
        for (int i = 0 ; i < 9 ; i ++)
        {
            if (x[i] % 4 != 0)//有不是12点的钟
                return ;
        }
        judge = true ;//全部调整完成
        return ;
    }
    for (int i = 0 ; i <= 3 ; i ++)
    {
        ans[num] = i ;//第(i+1)钟方式的数量
        for (int j = 0 ; j < 9 ; j ++)
        {
            x[j] += way[num][j] * i ;//改变
        }
        dfs(num + 1) ;//深搜
        if (judge)//此时已经调整完成,而调整方式有且只有一种,因此可以输出答案
            return ;
        for (int j = 0 ; j < 9 ; j ++)
        {
            x[j] -= way[num][j] * i ;//回溯
        }
    }
}
int main()
{
    for (int i = 0 ; i < 9 ; i ++)
    {
        cin >> x[i] ;
    }
    dfs(0) ;
    for (int i = 0 ; i < 9 ; i ++)
    {
        for (int j = 0 ; j < ans[i] ; j ++)
        {
            cout << i + 1 << ' ' ;
        }
    }
    cout << endl ;
}

 

你可能感兴趣的:(宽度优先搜索BFS 和深度优先搜索DFS的比较。。)