用不相交集合生成一个迷宫

  最近看到不相交集,使用不相交集写了一个生成迷宫算法,其实想法很简单,就来分享一下,首先我们对迷宫中的每个格子进行编号,从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);
                    }
                }
            }
        }
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值