两天前看的问题,今天总算做出来了.当然,是利用的下班后和午休时间来做的这题.码农996不容易的.

从题意来看,就是利用栈(Stack)结构加上泛型来解决迷宫问题.如果说问题本身的话,其实没什么难的,无非就是利用栈来代替递归来实现寻址.基本上能写递归就能做这题了.

那有人可能问了,说起来简单,那你还花两天时间?其实呢,这两天时间我主要花在了别的方面,比如,生成迷宫,并将其在控制台中画出来.这样这题才会显得有趣一些么不是.

第一步,实现迷宫类

迷宫就是由一堆小格子(Cell)组成的,所以我们先定义Cell,节省版面,我把getter和setter去掉了:
public class Cell {
private boolean w;
private boolean e;
private boolean n;
private boolean s;
}
然后是迷宫类,里面是一堆小格子:
import java.util.LinkedList;
public class Migong {
private Cell[][] cells = null;
}
这里呢,我用CellGen类来随机创建迷宫里的小格子:
import java.util.*;
public class CellsGen {
public static Cell[][] random(int row, int col) {
Cell[][] cells = new Cell[row][col];
for (int i = 0; i < cells.length; i++) {
for (int j = 0; j < cells[i].length; j++) {
cells[i][j] = new Cell();
}
}
breakWalls(cells, row, col);
return cells;
}
private static void breakWalls(Cell[][] cells, int maxRow, int maxCol) {
// 利用算法将小格子的墙打破,形成迷宫;具体代码这里就不贴了,没优化过,太长太繁琐 }
}

第二步,迷宫的展示

主要是在控制台中展示迷宫,这里我利用了制表符号,这两天工作之余的时间主要就是在算这个了
private static final char FULL_SPACE = ' ';
private static StringBuilder toStringBuilder(Cell[][] cells) {
StringBuilder sline = new StringBuilder();
int maxRow = 0;
int maxCol = 0;
for (int r = 0; r < cells.length; r++) {
maxRow = cells.length;
for (int c = 0; c < cells[r].length; c++) {
maxCol = cells[r].length;
if (c == 0) {
if (r == 0) {
sline.append('┌');
} else {
sline.append('├');
}
} else {
if (r == 0) {
sline.append('┬');
} else {
sline.append('┼');
}
}
if (cells[r][c].isN()) {
sline.append(FULL_SPACE);
} else {
sline.append("─");
}
if (c == cells[r].length - 1) {
if (r == 0) {
sline.append('┐');
} else {
sline.append('┤');
}
}
}
sline.append("\n");
for (int c = 0; c < cells[r].length; c++) {
if (cells[r][c].isW()) {
sline.append(FULL_SPACE);
} else {
sline.append("│");
}
sline.append(FULL_SPACE);
if (c == cells[r].length - 1) {
sline.append('│');
}
}
sline.append("\n");
if (r == cells.length - 1) {
for (int c = 0; c < cells[r].length; c++) {
if (c == 0) {
sline.append('└');
} else {
sline.append('┴');
}
sline.append("─");
if (c == cells[r].length - 1) {
sline.append("┘");
}
}
}
}
// ☆★ int index = ((maxCol) * 2 + 2) + 1;
sline.replace(index, index + 1, "☆");
index = (maxRow * 2 - 1) * ((maxCol) * 2 + 2) + maxCol * 2 - 1;
sline.replace(index, index + 1, "★");
return sline;
}
效果还是挺好的.Windows下用新宋体查看效果更好:
┌─┬─┬─┬─┬─┬─┬─┐
│☆      │     │
├─┼─┼─┼ ┼ ┼─┼ ┤
│       │ │   │
├ ┼─┼─┼─┼ ┼ ┼─┤
│ │ │ │ │ │   │
├ ┼─┼─┼─┼ ┼─┼ ┤
│ │ │ │ │ │ │ │
├ ┼─┼─┼─┼─┼ ┼ ┤
│   │ │ │ │   │
├─┼ ┼─┼─┼─┼ ┼─┤
│ │   │ │   │ │
├─┼─┼ ┼─┼ ┼─┼ ┤
│ │ │ │ │ │   │
├─┼─┼ ┼─┼ ┼─┼ ┤
│ │ │        ★│
└─┴─┴─┴─┴─┴─┴─┘

这个是新宋体下的效果:

第三步,寻址算法

这个就是本题的主要问题了.

先定义栈的接口和实现类,实现类我偷懒了,本来应该用数组的(出题人应该就是想考察数组实现栈吧)
public interface MyStack {
T pop();
T peek();
void push(T t);
}
实现类,就是这么个意思吧.
import java.util.ArrayList;
import java.util.List;
public class MySequentialStack implements MyStack {
private final List list = new ArrayList<>();
@Override
public T pop() {
if (!list.isEmpty()) {
return list.remove(list.size() - 1);
}
return null;
}
public T peek() {
if (!list.isEmpty()) {
return list.get(list.size() - 1);
}
return null;
}
@Override
public void push(T t) {
list.add(t);
}
}

然后是寻址算法, 放在Migong类里面,因为要存储x/y坐标,所以我用了一个Point类,和题目里要求的Point类不是同一个意思(题目里的被我换成了Cell).主要思路就是遍历所有的可能路径,直到走到终点.(好像是废话,具体还是看代码吧)

public void findPaths() {
if (cells == null) {
return;
}
int maxRow = cells.length;
int maxCol = 0;
if (maxRow > 0) {
maxCol = cells[0].length;
}
MyStack pointStack = new MySequentialStack<>();
pointStack.push(new Point(0, 0));
Point point = null;
char lastTriedDirection = ' ';
while ((point = pointStack.pop()) != null) {
if (point.getX() == maxCol - 1 && point.getY() == maxRow - 1) {
paths = new LinkedList<>();
do {
paths.addFirst(point);
} while ((point = pointStack.pop()) != null);
return;
}
Cell cell = cells[point.getY()][point.getX()];
char fromDirection = direction(pointStack.peek(), point);
Point next = null;
if (lastTriedDirection < 'e' && cell.isE() && fromDirection != 'e') {
next = new Point(point.getX() + 1, point.getY());
}
if (lastTriedDirection < 'n' && next == null && cell.isN() && fromDirection != 'n') {
next = new Point(point.getX(), point.getY() - 1);
}
if (lastTriedDirection < 's' && next == null && cell.isS() && fromDirection != 's') {
next = new Point(point.getX(), point.getY() + 1);
}
if (lastTriedDirection < 'w' && next == null && cell.isW() && fromDirection != 'w') {
next = new Point(point.getX() - 1, point.getY());
}
if (next == null) {
lastTriedDirection = direction(point, pointStack.peek());
} else {
pointStack.push(point);
pointStack.push(next);
lastTriedDirection = ' ';
}
}
}
private char direction(Point from, Point to) {
if (from == null || to == null) {
return ' ';
}
if (from.getX() > to.getX()) {
return 'e';
}
if (from.getX() < to.getX()) {
return 'w';
}
if (from.getY() > to.getY()) {
return 's';
}
if (from.getY() < to.getY()) {
return 'n';
}
return ' ';
}

第四步,输出路径

具体代码这里就不贴了,也是占了大头的琢磨时间.

直接贴效果图吧,同样,用新宋体看是最舒服的.

源码我扔到github上了:https://github.com/minghua-li/zhihu-migonggithub.com