并查集应用——生成随机迷宫

    在读《数据结构与算法分析Java语言描述一书中》,在并查集章节的最后,作者给出了一个并查集的应用:生成随机迷宫并查集应用——生成随机迷宫_第1张图片
此处作者给了思路,但是没有给出任何代码,所以自己实现了出来,先上最终效果图:
并查集应用——生成随机迷宫_第2张图片
页面是用的thymeleaf,用表格实现,方块区域与墙壁都是一个单元格,后端准备好样式直接在页面显示。
首先先造一个没有路的迷宫:
并查集应用——生成随机迷宫_第3张图片
相关代码:
public class Item {

//元素所在索引
private Integer index;

//元素宽度
private Integer width;

//元素高度
private Integer height;

//是否显示
private Boolean display;

//背景颜色
private String color;

//css样式,供页面显示
private String style;

//根据各属性生成style
public String generateStyle(){
    StringBuilder sb = new StringBuilder();
    sb.append("width:").append(width).append("px;");
    sb.append("height:").append(height).append("px;");
    sb.append("background-color: ").append(color).append(";");
    if(!display){
        sb.append("visibility:hidden");
    }

    this.style = sb.toString();
    return this.style;
}
/**
 * 方格类
 */
public class Square extends Item {

    // 上级方块的index, 并查集操作用
    private Integer value;

    public Square(){
        //并查集操作前,初始化为-1
        this.value = -1;
    }

    public Integer getValue() {
        return value;
    }

    public void setValue(Integer value) {
        this.value = value;
    }
}
/**
 * 墙壁类
 */
public class Wall extends Item {

    //墙壁分隔的方格所在的index
    private Integer squareIndex;

    //墙壁分隔的另一个方格所在的index
    private Integer anotherSquareIndex;

    public Integer getSquareIndex() {
        return squareIndex;
    }

    public void setSquareIndex(Integer squareIndex) {
        this.squareIndex = squareIndex;
    }

    public Integer getAnotherSquareIndex() {
        return anotherSquareIndex;
    }

    public void setAnotherSquareIndex(Integer anotherSquareIndex) {
        this.anotherSquareIndex = anotherSquareIndex;
    }
}
    //墙壁列表(只包括墙壁)
    private List walls = new ArrayList<>();
    //item列表(包括方块和墙壁)
    private List items = new ArrayList<>();
    //方块总行数
    final int rowCnt = config.getRowCnt();
    //方块总列数
    final int colCnt = config.getColCnt();
    //方块边长
    final int edgeLen = config.getEdgeLen();
    //总行数(包括墙壁)
    final int totalRowCnt = 2 * config.getRowCnt() - 1;
    //总列数(包括墙壁)
    final int totalColCnt = 2 * config.getColCnt() - 1;
    //起点位置下标
    final int startIndex = (config.getStartRow() * 2) * totalColCnt + config.getStartCol() * 2;
    //终点位置下标
    final int endIndex = (config.getEndRow() * 2) * totalColCnt + config.getEndCol() * 2;

    int count = 0;
    for(int i=0;i

这里分两种情况,偶数行包括方块和墙壁,奇数行只包含墙壁,方块和墙壁的样式不一样。

接着用并查集的搜索、合并方法生成迷宫,这里的思路是:
1.随机选取一面墙,并查看墙壁两边的方格是否连通。
2.如果不连通,拆掉这面墙。
3.如果连通,检查起点和终点是否连通,如果不连通则重复第一步,如果连通则循环结束。

//并查集生成迷宫
while(true){
    Random random = new Random();
    int i = random.nextInt(walls.size());

    Wall wall = walls.get(i);
    Integer wallIndex = wall.getSquareIndex();
    Integer otherWallIndex = wall.getAnotherSquareIndex();

    int i1 = find(wallIndex);
    int i2 = find(otherWallIndex);
    if(i1 == i2){
        int i3 = find(startIndex);
        int i4 = find(endIndex);
        if(i3 == i4){
            break;
        }
    } else {
        wall.setDisplay(false);
        union(i1, i2);
        walls.remove(wall);
        Vertex vertex1 = vMap.get(wallIndex);
        Vertex vertex2 = vMap.get(otherWallIndex);
        vertex1.addAdj(vertex2);
        vertex2.addAdj(vertex1);
    }
}

//*****************
//**disjoint sets**
//*****************
//查找
private int find(int x){
    Square square = (Square)items.get(x);

    if (square.getValue() < 0) {
        return x;
    } else {
        //普通查找
//            return find(square.getValue(), items);
        //路径压缩查找
        square.setValue(find(square.getValue()));
        return square.getValue();
    }
}

//合并
private void union(int root1, int root2) {
    // 普通求并
    //		s[root2] = root1;
//        items.get(root2).setValue(root1);

//        //按高度求并
    Square square2 = (Square)items.get(root2);
    Square square1 = (Square)items.get(root1);

    if (square2.getValue() < square1.getValue()) {
        square1.setValue(root2);
    } else {
        if (square1.getValue().intValue() == square2.getValue().intValue()) {
            square1.setValue(square1.getValue() - 1);
        }

        square2.setValue(root1);
    }
}

在算法运行期间,被拆掉的墙壁会被置为不显示,随后将每个Item的属性拼接成Stype并返回

// 生成最终样式
List> uiStyles = new ArrayList<>();
 List uiStyle = null;
 for(int i=0;i();
     }

     uiStyle.add(items.get(i).generateStyle());
 }
 uiStyles.add(uiStyle);

 return uiStyles;

最终返回给前端渲染

@GetMapping("execute")
public ModelAndView execute(Map map){
    Config config = new Config.Builder().rowCnt(21).colCnt(41).build();

    RandomLabyrinth randomLabyrinth = new RandomLabyrinth();
    List> lists = randomLabyrinth.generateLabyrinth(config);

    map.put("itemlist", lists);
    return new ModelAndView("labyrinth");
}

Config类里面包含一些可配置的参数

//总行数
private int rowCnt;
//总列数
private int colCnt;
//方块边长
private int edgeLen;
//起点所在行
private int startRow;
//起点所在列
private int startCol;
//终点所在行
private int endRow;
//终点所在列
private int endCol;

页面代码




    
    random labyrinth


    

你可能感兴趣的:(算法,Java)