三、最小生成树算法之Kruskal算法
数据结构中的Kruskal算法:将每个顶点视为一棵树,将所有边加入集合,从边的集合中依次选择权最小的边,如果该边两端的顶点属于不同的树,将两棵树合并,并把这条边记录在一个新的集合中;如果两端顶点属于一棵树,舍弃这条边,如此循环直到新集合中边的数量等于顶点数量-1
迷宫算法中由于所有边的权相等,且需要随机性,所以与数据结构中的Kruskal算法有较大的不同:
将每个顶点视为一棵树,将所有边加入集合,从边的集合中随机选择一条边,如果该边两端的顶点属于不同的树,将两棵树合并,将这条边移除集合,如果属于相同树,直接舍弃这条边,直到边的集合为空,即所有边都被访问过,结束构造
效果如图
此算法与前两个不同,此算法是面向边来构造迷宫,而前两个是面向顶点构造迷宫。
由于树的合并问题,我们需要加入并查集这一概念,即在顶点类中加入一个root指针指向它的父顶点,如果root为空说明该顶点是根,合并树就是让其中一棵树根的root指向另一棵树的根。
class Map
{
public:
int val;//对访问过的顶点进行标记
int x;//坐标
int y;
vector<Map*>next;//存放相连顶点
Map*root;//指向父顶点
Map()
{
root = NULL;
val = 0;
x = 0;
y = 0;
}
Map(int c, int r)
{
root = NULL;
val = 0;
x = c;
y = r;
}
void push(Map* m)
{
next.push_back(m);
}
void erase(Map* m)
{
for (auto it = next.begin(); it != next.end(); it++)
{
if ((*it)->x == m->x&&(*it)->y == m->y)
{
next.erase(it);
}
}
}
};
由于要面向边构造迷宫,还需要加入边类
class Side
{
public:
Map*first;
Map*second;//两端的顶点
Side()
{
first = NULL;
second = NULL;
}
Side(Map*m1, Map*m2)
{
first = m1;
second = m2;
}
};
Kruskal算法函数
void Kruskal()
{
vector<Side>Lib;//把所有边加入容器
for (int i = 0; i < ROW; i++)
{
for (int j = 0; j < COL - 1; j++)
{
Lib.push_back(Side(map[j][i], map[j + 1][i]));
}
}
for (int i = 0; i < COL; i++)
{
for (int j = 0; j < ROW - 1; j++)
{
Lib.push_back(Side(map[i][j], map[i][j + 1]));
}
}
while (!Lib.empty())//所有边都访问过就结束构造
{
Sleep(0);
int select = rand() % Lib.size();随机选择一条边
Map*temp1 = Lib[select].first;//用两个临时指针找两端点分属哪个根
Map*temp2 = Lib[select].second;
while (temp1->root != NULL)
{
temp1 = temp1->root;
}
while (temp2->root != NULL)
{
temp2 = temp2->root;
}
if (temp1->x == temp2->x && temp1->y == temp2->y)//根相同即树相同,将这条边移出容器
{
Lib.erase(Lib.begin() + select);
}
else
{
temp1->root = temp2;//根不同就合并两棵树
Lib[select].first->push(Lib[select].second);
Lib[select].second->push(Lib[select].first);//将自己加入对方的相连容器中
int x = Lib[select].first->x;
int y = Lib[select].first->y;
int x2 = Lib[select].second->x;
int y2 = Lib[select].second->y;
//以下代码根据两顶点的相对位置移除它们之间的墙
if (y - 1 == y2)
{
solidrectangle(x*(RODE + WALL) - RODE / 2, y*(RODE + WALL) - RODE / 2 - WALL, x*(RODE + WALL) + RODE / 2, y*(RODE + WALL) - RODE / 2);
}
else if(y + 1 == y2)
{
solidrectangle(x*(RODE + WALL) - RODE / 2, y*(RODE + WALL) + RODE / 2, x*(RODE + WALL) + RODE / 2, y*(RODE + WALL) + RODE / 2 + WALL);
}
else if (x - 1 == x2)
{
solidrectangle(x*(RODE + WALL) - RODE / 2 - WALL, y*(RODE + WALL) - RODE / 2, x*(RODE + WALL) - RODE / 2, y*(RODE + WALL) + RODE / 2);
}
else if (x + 1 == x2)
{
solidrectangle(x*(RODE + WALL) + RODE / 2, y*(RODE + WALL) - RODE / 2, x*(RODE + WALL) + RODE / 2 + WALL, y*(RODE + WALL) + RODE / 2);
}
Lib.erase(Lib.begin() + select);//这条边已访问过了,移除它
}
}
}
寻路效果如图