最近看到不相交集,使用不相交集写了一个生成迷宫算法,其实想法很简单,就来分享一下,首先我们对迷宫中的每个格子进行编号,从0到n,先画一个n*m的格子版,然后删除一些边打通各个格子,使之变成连通,我们把边分成横边和纵边,我们可以通过计算,标记打通一条边会使两个格子变成连通,然后用不想交集合进行标记。最后使得所有边都连通起来即可:
具体操作:先把所有的边编号,放入一个数组内,然后用随机数打乱整个数组,接着对这个数组进行遍历,检测一条边连接的两个小方格,若其不连通,即不在一同一个不想交集合内,则打通他们,否则则不做任何动作,知道所有格子都被遍历完成,得到的边的数组就是生成迷宫了
不相交集源代码:
/**
* Created by v on 16-12-13.
*/
public class DisjSets {
private int[] s;
public DisjSets(int numElements){
s=new int[numElements];
for(int i=0;i<s.length;i++){
s[i]=-1;
}
}
public void union(int root1,int root2){
if(s[root1]<s[root2]){
s[root2]=root1;
}else{
if(s[root1]==s[root2]){
s[root2]--;
}
s[root1]=root2;
}
}
public int find(int x){
if(s[x]<0){
return x;
}else {
return s[x]=find(s[x]);
}
}
}
迷宫后台算法:
import java.util.Random;
/**
* Created by v on 16-12-13.
*/
public class Labyrinth {
private boolean[][][] sides;
private DisjSets disjSets;
/**
*
* 对于一个n*m的迷宫,纵向边共(1+n)*(1+m)条,横向边也一样,
* 因此我们用数组的最后一位表示纵横,z=0为横,z=1为纵
*
* @param rows 迷宫格子的行数
* @param lines 迷宫格子的列数
*/
public Labyrinth(int lines, int rows){
sides =new boolean[lines+1][rows+1][2];
disjSets=new DisjSets(rows*lines);
//去除边框处露出来的边
for(int i=0;i<=rows;i++){
sides[lines][i][0]=true;
}
for(int i=0;i<=lines;i++){
sides[i][rows][1]=true;
}
build(lines,rows);
//打通入口与出口
sides[0][0][1]=true;
sides[lines][rows-1][1]=true;
}
//产生一个迷宫
private void build(int lines,int rows){
int[] randomNumbers=new int[rows*lines*2];
//先生成一个包含所有边的编号的数组
for(int i=0;i<randomNumbers.length;i++){
randomNumbers[i]=i;
}
Random random=new Random();
//对整个数组进行打乱处理
for(int i=0;i<randomNumbers.length;i++){
swap(randomNumbers,i,random.nextInt(randomNumbers.length));
}
//对迷宫的每一条边进行处理,若边的两头未连通,则删除边
for(int i=0;i<randomNumbers.length;i++){
int x=randomNumbers[i]%lines;
int z=randomNumbers[i]/rows/lines;
int y=(randomNumbers[i]-z*rows*lines)/lines;
//对于位于边界的边,我们不处理
if(y==0&&z==0){continue;}
if(x==0&&z==1){continue;}
//尝试打通这一条边
if(z==0){
int root1=disjSets.find(x+lines*y);
int root2=disjSets.find(x+lines*(y-1));
if(root1!=root2){
disjSets.union(root1,root2);
sides[x][y][z]=true;
}
}else {
int root1=disjSets.find(x+lines*y);
int root2=disjSets.find(x-1+lines*y);
if(root1!=root2){
disjSets.union(root1,root2);
sides[x][y][z]=true;
}
}
}
}
//交换两个值
private final void swap(int[] array,int index1,int index2){
int temp=array[index1];
array[index1]=array[index2];
array[index2]=temp;
}
//返回迷宫的某一个边是否被打通,若被打通则返回true
public boolean getSide(int x,int y,int z){
return sides[x][y][z];
}
public int getLines(){
return sides.length;
}
public int getRows(){
return sides[0].length;
}
}
迷宫显示的ui:
import javax.swing.*;
import java.awt.*;
/**
* Created by v on 16-12-13.
*/
public class LabyrinthUI extends JApplet {
LabyrinthPanel panel;
public LabyrinthUI(){
panel=new LabyrinthPanel(40,30);
add(panel);
}
@Override
public void init() {
super.init();
setSize(600,400);
}
private class LabyrinthPanel extends JPanel{
private Labyrinth labyrinth;
public LabyrinthPanel(int lines,int rows){
labyrinth=new Labyrinth(lines,rows);
repaint();
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for(int i=0;i<labyrinth.getLines();i++){
for(int j=0;j<labyrinth.getRows();j++){
if(!labyrinth.getSide(i,j,0)){
g.drawLine(50+i*10,50+j*10,
50+i*10+10,50+j*10);
}
if(!labyrinth.getSide(i,j,1)){
g.drawLine(50+i*10,50+j*10,
50+i*10,50+j*10+10);
}
}
}
}
}
}