盲目搜索算法(DFS、BFS、DFS-ID)

Source

目录

概念

BFS

DFS

DFS-ID


概念

盲目搜索算法指的是不使用领域知识的不知情搜索算法。主要包括三种算法:深度优先搜索(DFS)广度优先搜索(BFS)迭代加深(DFS-ID)的深度优先搜索

当树很深、分支因子不大、在树中解出现的位置相对较深时,选择深度优先搜索。

当搜索树的分支因子不太大、在树中解出现的位置在合理的深度级别、路径不是非常深的时候,选择广度优先搜索。

在深度优先和广度优先的基础上,结合二者形成了一种全新的算法:迭代加深的深度优先

PS: 简单的说就是不断扩大搜索深度的深度优先。

使用DFS-ID若没有找到目标,就会执行另一个DFS,深度界限为1,每次迭代深度界限加1,搜索都要重新开始。

BFS是完备和优选的(在各种约束下),但过量的空间需求使其在应用上受到阻碍。

DFS既不是完备也不是优选的,DFS可能变得非常长或迷失在无限的路径中,但是空间需求合理。

DFS-ID同时具备DFS和BFS的有利性,即DFS的合理空间以及BFS的完备和优选,但是每次深度更新都需要把之前探索过的路径重新探索一遍,会存在非常大的时间浪费。

BFS

思路

BFS和DFS都需要准备一个close 标志,表示哪些点已经走过了,不需要再走了,也叫visit 图。

BFS 的基本逻辑就是,遍历周边的4个点,然后把4个点都放在队列里,然后出队首,重新把新的节点的周围4个点放进去,直到遍历到结束点。

代码

#define MaxSize 1000
 
// 边节点
typedef struct ANode
{
	// 边所指的终点顶点
	int adjvex;
	// 下一条边,邻域,指向下一个邻接点
	struct ANode * nextarc;
	// 权值
	int weight;
}ArcNode;
 
// 顶点
typedef struct Vnode
{
	// 数据域
	int data;	
	// 指向的第一条边
	ArcNode * firstarc;
}VNode;
 
// 邻接表
typedef struct
{
	VNode adjlist[MaxSize];
	// n为顶点数,e为边数
	int n;
	int e;	
}Graph;
 
// G为邻接点指针,V为初始访问节点
void BFS(Graph * G, int v)
{
	// 边节点
	ArcNode * p;
	// 访问数组
	int visited[MaxSize];
	// 队列
	int Qu[MaxSize];
	// 初始化循环队列
	int front = 0, rear = -1;
	// 当前节点的下标
	int w;
 
	// 初始化访问数组
	for(int i = 0; i < G->n; i++)
	{
		visited[i] = 0;
	}
 
	// 访问第一个节点
	printf("%d\n", v);
	visited[v] = 1;
 
	// 第一个节点入队
	rear = (rear + 1) % MaxSize;
	Qu[rear] = v;
 
	// 开始广度优先遍历
	while(front != rear)
	{
		front = (front + 1) / MaxSize;
		// 出队
		w = Qu[front];
		// 使p指向顶点的第一个边节点
		p = G->adjlist[w].firstarc;
 
		// 循环访问完w的所有边节点
		while(p != NULL)
		{
			if(visited[p->adjvex] == 0)
			{
				// 访问节点
				printf("%d\n", p->adjvex);
				visited[p->adjvex] = 1;
 
				// 入队
				rear = (rear + 1) % MaxSize;
				Qu[rear] = p->adjvex;
			}
 
			p = p -> nextarc;
		}
	}
 
}

DFS

思路

深度优先算法的核心是栈,就是说把一个点周围4个节点先放在栈里,然后出栈顶,重新把周围的4个节点放在栈里,直到找到结束点。

代码

//利用C++实现深度优先搜索算法,如有疑问请联系
//作者:cclplus 邮箱:maxwell970710@gmail.com
#include<iostream>
#include<vector>
#include<stack>
#include<memory.h>
 
using namespace std;
 
vector<vector<int>> tree;//声明一个二维向量
int flag[10];//用于搜索到了节点i的第几个节点
stack <int>stk;//声明一个堆栈
int ar_tree[8] = { 1,1,1,3,5,3,5,7 };
void DFS(int node) {
	cout << node <<" ";
	if (flag[node] == 0) {
		stk.push(node);
	}
	int temp;
	//判断node的子节点是否搜索完成
	if (flag[node] < tree[node].size()) {
		temp = tree[node][flag[node]];
		flag[node]++;
		DFS(temp);
	}
	else {//若已经完成
		stk.pop();//弹出子节点
		if (!stk.empty()) {//若堆栈不为空
			temp = stk.top();//取此时的栈顶元素,即为node的上一级节点
			DFS(temp);
		}
	}
}
int main() {
	ios::sync_with_stdio(false);
	memset(flag, 0, sizeof(flag));
	register int i,temp;
	tree.resize(10);//图中的数共有九个节点
	//生成树
	for (i = 2; i <=9; i++) {
		temp = ar_tree[i - 2];
		tree[temp].push_back(i);//表示第i个节点为第temp个节点的子节点
	}
	//DFS
	cout << "DFS过程:" << endl;
	DFS(1);
	cout << endl;
	return 0;
}

DFS-ID

思路

所谓深度迭代,就是设置一个深度tag,一旦超过该深度就不再将新节点入栈,如果没有找到结束节点就深度tag++,然后重来一遍。

代码

bool DLS(GrapheMat* graphe, Node* source, NomSom but, int limit) {
    bool found = false;
    printf("%s\n", (char*)source->etat);
    if (strcmp((char*)source->etat, (char*)but) == 0) {
        return true;
    }
    if (limit > 0) {
        List* listSon = nodeSon(graphe, source);
        while(!listEpmty(listSon)) {
            Node* son = (Node*)popList(listSon);
            if (DLS(graphe, son, but, limit-1)) {
                return true;
            }
        }
    }
    return false;
}

bool IDDLS (GrapheMat* graphe, NomSom goal, int limit) {
    bool found = false;
    node* source = createNode(graphe, graphe->nomS[0]);
    for (int i = 0; i <= limit; i++) {
        printf("/nLimit : %d\n", i);
        DLS(graphe, source, goal, i);
    }
    return false;
}