可视化对比搜索算法(DFS/BFS)

简介:

深度优先搜索(DFS)和广度优先搜索(BFS)是两大最基本的搜索算法,本程序旨在用可视化的方法帮初学者更好的理解两个搜索算法的区别。

本程序可以随机生成带有障碍物的地图和起点,并分别用两种搜索算法进行可视化遍历。

注意:

本程序在 Visual Studio 2022 上编译通过,需要将项目字符集更改为多字节字符集才能运行

运行本程序需要安装第三方库EasyX,下载网址:https://easyx.cn/

// 编译环境:Visual Studio 2022,EasyX_20220116
// 作者:lasersword10

#include<easyx.h>
#include<cstdio>
#include<cstring>
#include<stdlib.h>
#include<time.h>
#include<utility>
#include<queue>
using namespace std;

#define WINDOW_WIDTH 960	// 窗口宽度
#define WINDOW_HEIGHT 640	// 窗口高度

#define MAP_COL 20		// 地图列数
#define MAP_ROW 20		// 地图行数

#define PASS 0			// 允许通行
#define IMPASS 1		// 禁止通行
#define VISITED 2		// 已访问
#define VISITING 3		// 正在访问

#define IMPASS_NUM 40	// 禁止通行的数目

#define SIDE_LENGTH	30	// 每一小格的边长

#define MAP_WIDTH SIDE_LENGTH * MAP_COL	// 地图宽
#define MAP_HEIGHT SIDE_LENGTH * MAP_ROW	// 地图高

#define BUTTON_WIDTH 300	// 按钮宽
#define BUTTON_HEIGHT 200	// 按钮高
#define BUTTON_INTERVAL 100	// 按钮间隔

#define DELAYTIME 10	// 延时时间

struct rect // 矩形
{
	int x, y;
	int color;
};

struct button // 按钮
{
	int x, y, w, h;
	const char text[100];
	int color;
};

pair<int, int> randxy() // 生成一个随机点
{
	int x = rand() % MAP_COL + 1;
	int y = rand() % MAP_ROW + 1;
	return make_pair(x, y);
}

void initmap(int m[MAP_ROW + 5][MAP_COL + 5]) // 初始化地图
{
	for (int i = 1; i <= IMPASS_NUM; i++)
	{
		pair<int, int> p = randxy();
		m[p.second][p.first] = IMPASS;
	}
}

void rebackmap(int m[MAP_ROW + 5][MAP_COL + 5]) // 回溯整张地图
{
	for (int i = 1; i <= MAP_ROW; i++)
		for (int j = 1; j <= MAP_COL; j++)
			if (m[i][j] != IMPASS) m[i][j] = PASS;
}

void printmap(int m[MAP_ROW + 5][MAP_COL + 5]) // 控制台输出地图
{
	for (int i = 1; i <= MAP_ROW; i++)
	{
		for (int j = 1; j <= MAP_COL; j++)
		{
			printf("%d ", m[i][j]);
		}
		printf("\n");
	}
	printf("\n");
}

void paintrect(rect r) // 绘画一个矩形
{
	setfillcolor(r.color);
	fillrectangle(r.x, r.y, r.x + SIDE_LENGTH, r.y + SIDE_LENGTH);
}

void paintbutton(button bt) // 绘画一个按钮
{
	BeginBatchDraw();
	setlinestyle(PS_SOLID, 5);
	setlinecolor(BLACK);
	setfillcolor(bt.color);
	settextcolor(BLACK);
	settextstyle(70, 50, "黑体");
	fillroundrect(bt.x, bt.y, bt.x + bt.w, bt.y + bt.h, 10, 10);
	int xx = bt.x + (bt.w - textwidth(bt.text)) / 2;
	int yy = bt.y + (bt.h - textheight(bt.text)) / 2;
	outtextxy(xx, yy, bt.text);
	EndBatchDraw();
}

bool isbuttondown(button bt, ExMessage m) // 判断点击是否在按钮内
{
	if (m.x >= bt.x && m.x <= bt.x + bt.w && m.y >= bt.y && m.y <= bt.y + bt.h)
		return true;
	return false;
}

void paintmap(int m[MAP_ROW + 5][MAP_COL + 5]) // 绘制地图
{
	int mapx = (WINDOW_WIDTH - MAP_WIDTH) / 2; // 地图左上角的 x
	int mapy = (WINDOW_HEIGHT - MAP_HEIGHT) / 2; // 地图左上角的 y
	BeginBatchDraw();
	setlinecolor(WHITE);
	setlinestyle(PS_SOLID);
	for (int i = 1; i <= MAP_ROW; i++)
	{
		for (int j = 1; j <= MAP_COL; j++)
		{
			rect tmp;
			switch (m[i][j])
			{
			case PASS:
				tmp = { mapx + (j - 1) * SIDE_LENGTH, mapy + (i - 1) * SIDE_LENGTH, GREEN };
				paintrect(tmp);
				break;
			case IMPASS:
				tmp = { mapx + (j - 1) * SIDE_LENGTH, mapy + (i - 1) * SIDE_LENGTH, BLACK };
				paintrect(tmp);
				break;
			case VISITED:
				tmp = { mapx + (j - 1) * SIDE_LENGTH, mapy + (i - 1) * SIDE_LENGTH, RED };
				paintrect(tmp);
				break;
			case VISITING:
				tmp = { mapx + (j - 1) * SIDE_LENGTH, mapy + (i - 1) * SIDE_LENGTH, RED };
				paintrect(tmp);
				break;
			default:
				break;
			}
		}
	}
	EndBatchDraw();
}

int nxt[4][2] = { {-1, 0}, {0, 1}, {1, 0}, {0, -1} }; // 方向向量

void dfs(int m[MAP_ROW + 5][MAP_COL + 5], int x, int y) // 深度优先搜索
{
	settextstyle(70, 50, "黑体");
	settextcolor(BLACK);
	outtextxy(0, WINDOW_HEIGHT / 3, "DFS");
	paintmap(m);
	printmap(m);
	Sleep(DELAYTIME);
	for (int i = 0; i < 4; i++)
	{
		int xx = x + nxt[i][0];
		int yy = y + nxt[i][1];
		if (xx<1 || xx>MAP_ROW || yy<1 || yy>MAP_COL || m[xx][yy]) continue;
		m[xx][yy] = VISITING;
		dfs(m, xx, yy);
	}
	m[x][y] = VISITED;
}

void bfs(int m[MAP_ROW + 5][MAP_COL + 5], int x, int y) // 广度优先搜索
{
	queue<pair<int, int> > Q;
	Q.push(make_pair(x, y));
	m[x][y] = VISITING;
	while (!Q.empty())
	{
		pair<int, int> a = Q.front();
		Q.pop();
		settextstyle(70, 50, "黑体");
		settextcolor(BLACK);
		outtextxy(0, WINDOW_HEIGHT / 3, "BFS");
		paintmap(m);
		printmap(m);
		m[a.first][a.second] = VISITED;
		Sleep(DELAYTIME);
		for (int i = 0; i < 4; i++)
		{
			int xx = a.first + nxt[i][0];
			int yy = a.second + nxt[i][1];
			if (xx<1 || xx>MAP_ROW || yy<1 || yy>MAP_COL || m[xx][yy]) continue;
			Q.push(make_pair(xx, yy));
			m[xx][yy] = VISITING;
		}
	}
}

int main()
{
	int map[MAP_ROW + 5][MAP_COL + 5];
	int step = 1;

	srand((unsigned)time(NULL)); // 生成随机数种子

	// 地图初始化
	memset(map, PASS, sizeof(map));
	initmap(map);
	printmap(map);

	// 窗口初始化
	initgraph(WINDOW_WIDTH, WINDOW_HEIGHT);
	setbkcolor(WHITE);
	setbkmode(TRANSPARENT);
	cleardevice(); // 清屏

	// 主界面按钮
	button b1 = { (WINDOW_WIDTH - 2 * BUTTON_WIDTH - BUTTON_INTERVAL) / 2, (WINDOW_HEIGHT - BUTTON_HEIGHT) / 2, BUTTON_WIDTH, BUTTON_HEIGHT, "DFS", WHITE };
	button b2 = { (WINDOW_WIDTH + BUTTON_INTERVAL) / 2, (WINDOW_HEIGHT - BUTTON_HEIGHT) / 2, BUTTON_WIDTH, BUTTON_HEIGHT, "BFS", WHITE };

	pair<int, int> p = randxy(); // 生成随机起点

	// 消息循环
	while (true)
	{
		if (step == 1) // 处于主界面阶段绘制按钮
		{
			paintbutton(b1);
			paintbutton(b2);
		}
		ExMessage msg;
		if (peekmessage(&msg, EM_MOUSE))
		{
			switch (msg.message)
			{
			case WM_LBUTTONDOWN:// 鼠标左键按下
				if (step != 1) break; // 不在主界面时跳过
				if (isbuttondown(b1, msg))
				{
					step = 2; // 进入搜索阶段
					cleardevice();
					rebackmap(map);
					map[p.second][p.first] = VISITING;
					dfs(map, p.second, p.first);
					Sleep(1000);
					cleardevice();
					step = 1; // 回到主界面
				}
				else if (isbuttondown(b2, msg))
				{
					step = 2; // 进入搜索阶段
					cleardevice();
					rebackmap(map);
					bfs(map, p.second, p.first);
					Sleep(1000);
					cleardevice();
					step = 1; // 回到主界面
				}
				break;
			case WM_MOUSEMOVE:// 鼠标移动
				if (step != 1) break; // 不在主界面时跳过
				if (isbuttondown(b1, msg))
				{
					b1.color = YELLOW;
					paintbutton(b1);
				}
				else
				{
					b1.color = WHITE;
					paintbutton(b1);
				}
				if (isbuttondown(b2, msg))
				{
					b2.color = YELLOW;
					paintbutton(b2);
				}
				else
				{
					b2.color = WHITE;
					paintbutton(b2);
				}
				break;
			}
		}
	}

	getchar();
	closegraph();
	return 0;
}

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值