第四周的作业是解决8-Puzzle的问题,该问题是使用Priority Queue的一个实例。原文地址http://coursera.cs.princeton.edu/algs4/assignments/8puzzle.html
模型描述:
在九宫格里,有从1-8两两互不相等的整数,加上一个空格(用0表示)。我们需要每次将空格和四周的一个格子交换位置,最后形成从(0,0)到(2,1)为1-8顺序的九宫格。类似于华容道游戏,区别在于华容道每个格子有大小。
8 1 3 8 1 3 8 1 8 1 3 8 1 3 4 2 4 2 4 2 3 4 2 4 2 5 7 6 5 7 6 5 7 6 5 7 6 5 7 6 previous search node neighbor neighbor neighbor (disallow)
如图所示,从左到右显示出每步的步骤,最后完成目标。
解决方法如下:
首先给出如下定义:Board:每次形成的九宫格,
Neighbour:能够从给定Board只移动一个格子一次就达到的新Board,
Previous:该Board从由之前的哪个Board变换而来,
需要做的事:
1.首先构造Board类,该类具有如下API
public class Board { public Board(int[][] blocks) // construct a board from an N-by-N array of blocks // (where blocks[i][j] = block in row i, column j) public int dimension() // board dimension N public int hamming() // number of blocks out of place public int manhattan() // sum of Manhattan distances between blocks and goal public boolean isGoal() // is this board the goal board? public Board twin() // a board that is obtained by exchanging any pair of blocks public boolean equals(Object y) // does this board equal y? public Iterable2.构造Solver类,neighbors() // all neighboring boards public String toString() // string representation of this board (in the output format specified below) }
public class Solver { public Solver(Board initial) // find a solution to the initial board (using the A* algorithm) public boolean isSolvable() // is the initial board solvable? public int moves() // min number of moves to solve initial board; -1 if unsolvable public Iterablesolution() // sequence of boards in a shortest solution; null if unsolvable public static void main(String[] args) // solve a slider puzzle (given below) }
实现:
用MinPriorityQueue实现A*算法,MinPQ和课堂上讲的不一样,课堂上讲的MaxPQ包含删除Max,找到Max的方法。MinPQ类似,首先将初始的Board插入MinPQ,然后删除min,既是该初始Board,然后插入min的邻居到MinPQ里面,再在新的删除min,再插入min的邻居,最后直到min就等于Goal。
该例子应用PQ,PQ的作用比较简单,就是能够快速提取出min或者max的已经sortted的数组。
构建的时候使用Binary Heap,将大的元素排在father。如果给出一个PQ,使用Heapsort就能对它进行排序。
实行起来分工明确,比较简单,但是需要很多注意的地方:
1.当我们在构造两个不同的对象需要用到同一个参数时,要对该参数构造aux,然后用aux进行。
eg, 给定一个数组test,现在需要构造两个Board,其中一个是Board b1 = new Board(test); 第二个是Borad b2 = new Board(test)。
但是如果在b1里面进行操作,比如b1.blocks[0][0] = 0, 那么同样在b2里面的也会变化。原因是b1.blocks是个reference, 指向的是test,更改b1.blocks的时候,其实就是在改动test,而b2又是利用test构造的对象,所以b2也会发生变化。要正确使用,在构造b1的时候就需要aux,
int[][] aux = test; Board b1 = new Board(test);
此时如果再对b1.blocks 进行更改,就不会对test产生影响,再用
Board b2 = new Board(test)构造的b2就是一个没有被b1影响的新Board;
2.在进行数组比较的时候,不能直接使用 a[] == b[]的符号,一维数组使用Array.equals(a[], b[]). 二维数组需要自己写for for 循环比较;
3.在把某个对象转化成String输出时,可以使用
StringBuilder s = new StringBuilder();
s.append(N+"\n"); 既是加入N和换行,
s.append(String.format("%2d", blocks[i][j]));既是加上将int转换为String格式的字符。
4.找二维数组某个特定值x的坐标可以用如下方法:(需要用两个break来跳出for循环)
int blankI = 0, blankJ = 0;
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
if (blocks[i][j] == x) {
blankI = i;
blankJ = j;
break;
}
}
if (blocks[blankI][blankJ] == 0) break;
}
5.当对于某个对象的一个方法能产生很多其他对象的时候,可以选择return Iterable
public Iterable<Board> neighbors()
比如上述,只需要新建一个Queue,然后把产生的neighbors全都放入Queue里面,再返回这个Queue。之后就能使用foreach将neighbors方法产生的所有neighbor都遍历出来;