可视化对比搜索算法(DFS/BFS)
2022-4-23 ~ 2022-4-24
(0)
简介:
深度优先搜索(DFS)和广度优先搜索(BFS)是两大最基本的搜索算法,本程序旨在用可视化的方法帮初学者更好的理解两个搜索算法的区别。
注意:本程序在 Visual Studio 2022 上编译通过,需要将项目字符集更改为多字节字符集才能运行
源码:
// 编译环境: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;
}
添加评论
取消回复