当我们使用计算机来解决问题时,一般需要这几个步骤:首先从该具体问题抽象出一个数学模型,然后设计或者选择一个解决此数学模型的算法,然后编写出程序进行调试、测试,直到得到最终的解答。这是是将数学思维用计算机实现的一个普遍逻辑,然而,随着计算机的发展,并非所有的问题都可以用简单的数学方程式解决。因此,解决这类问题的关键不再是数学分析和计算方法,而是要设计出合适的数据结构,才能有效解决此类问题。
下面来一个具体问题体现数据结构的重要作用:
用运回溯算法求解八皇后问题,该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。
方法一: 定义皇后位置类来处理皇后位置数据的实现方式;
解题思路:已在代码中注明。
package algorithm; import java.util.LinkedList; /** * 回溯算法:八皇后问题 * 该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出: * 在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。 */ public class EightQueen { private static final int SIZE = 8; //皇后的个数,此处设置8,表示8个皇后 private static int count = 0; //记录摆放的方式数 /** * 定义位置的数列结构,用于表示皇后的摆放位置 * */ static class Location { int x; //对应棋盘的列 int y; //对应棋盘的行 Location(int x, int y) { this.x = x; this.y = y; } @Override public String toString() { return "(" + x + "," + y +")"; } } /** * 主要回溯法函数 * @param list * @param x //对应棋盘的列 * @param y //对应棋盘的行 */ private static void eightQueen(LinkedList<Location> list, int x, int y) { if(list.size() == SIZE) { //当list元素个数为8时,表示8个皇后都摆放完毕,打印后即可退出函数。 printLocation(list); //打印皇后摆放方式 return ; } for (int i = x; i < SIZE; i++) { Location loc = new Location(i,y); if(isLegalLoc(list,loc)) { list.offer(loc); //将第y行的皇后摆放好 eightQueen(list,0,y+1); //开始摆放y+1行的皇后,同样从第0列开始摆放 list.pollLast(); //每次摆放完一个皇后后,都要将其撤回,再试探其它的摆法。(pollLast() 获取并移除此列表的最后一个元素) } } } /** * 判断位置为loc的皇后是否合法 * @param list * @param loc * @return */ private static boolean isLegalLoc(LinkedList<Location> list, Location loc) { for (Location each : list) { if(loc.x == each.x || loc.y == each.y) { //判断是否在同一行或同一列 return false; }else if(Math.abs(loc.x - each.x) == Math.abs(loc.y - each.y)) { //判断是否在同一对角线或反对角线上 return false; } } return true; } /** * 打印皇后摆放位置方式 * @param list */ private static void printLocation(LinkedList<Location> list) { for (Location each : list) { System.out.print(each.toString()+" "); } System.out.println(); count ++; } public static void main (String[] args) { LinkedList<Location> list = new LinkedList<Location>(); eightQueen(list,0,0); //从棋盘的第0行第0列开始 System.out.println("八皇后共有"+count+"种摆放方式"); } }
方法二: 以一维数组的方式来处理皇后位置数据的实现方式;
解题思路:已在代码中注明。
package algorithm; /** * 回溯算法:八皇后问题 * @author 86135 * */ public class EightQueen2 { public static int num = 0; //累计方案 public static final int MAXQUEEN = 8; public static int[] cols = new int[MAXQUEEN]; //定义数组,表示8列棋子皇后摆放的位置 /** * @param n 表示填第n列的皇后 * */ public void getCount(int n){ boolean[] rows = new boolean[MAXQUEEN]; for(int i = 0;i < n;i++){ rows[cols[i]] = true; int d = n - i; //计算差值 //正斜方向 if(cols[i] - d >= 0){ rows[cols[i]- d] = true; } //反斜方向 if(cols[i] + d <= (MAXQUEEN - 1)){ rows[cols[i] + d] = true; } } for(int i = 0;i < MAXQUEEN;i++){ if(rows[i]){ continue; } cols[n] = i; if(n < MAXQUEEN - 1){ getCount(n + 1); }else{ //找到一套完整的方案 num++; printQueen(); } } } public void printQueen(){ System.out.println("第" + num + "种方案"); for(int i = 0;i < MAXQUEEN;i++){ for(int j = 0;j < MAXQUEEN;j++){ if(i == cols[j]) System.out.print("Q "); else System.out.print("+ "); } System.out.println(); } } public static void main(String[] args){ EightQueen2 q = new EightQueen2(); q.getCount(0); } }
方法三: 以二维数组的方式来处理皇后位置数据的实现方式;
解题思路:由上往下依次向下遍历,那么我们只需要验证当前格子的上方,左斜上方和右斜上方的位置是否有皇后(因为如果由上往下遍历的时候,在当前行的下方是一定没有皇后的,所以我们只需要验证三个方向),如过三个方向都没有皇后,那么就可以确定当前位置可以放皇后
package algorithm; /** * 回溯算法:八皇后问题 * * 解题思路:由上往下依次向下遍历,那么我们只需要验证当前格子的上方,左斜上方和右斜上方的位置是否有皇后 * (因为如果由上往下遍历的时候,在当前行的下方是一定没有皇后的,所以我们只需要验证三个方向), * 如过三个方向都没有皇后,那么就可以确定当前位置可以放皇后 * @author 86135 * */ public class EightQueen3 { //定义 8*8 矩阵(二维数组) public static int[][] map = new int[8][8]; public static int count = 1; /** * 显示棋盘 */ public static void show() { System.out.println("第"+count+"中排列方式"); for (int i = 0; i < 8; i++) { for (int j = 0; j < 8; j++) { System.out.print(map[i][j] +" "); } System.out.println(); } count++ ; } /** * 检验方法(验证该位置是否可以放皇后) * @param row * @param col * @return */ public static boolean check(int row,int col) { //上面(行减小 列不变) for (int i = row-1; i >= 0; i--) { if(map[i][col] == 1) { return false; } } //左斜上 (行减小 列减小) for (int i = row-1,j = col-1; i >= 0 && j >= 0; i--,j--) { if (map[i][j] == 1) { return false; } } //右斜上 (行减小 列增加) for (int i = row-1,j = col+1; i >= 0 && j < 8; i--,j++) { if (map[i][j] == 1) { return false; } } return true; } /** * 皇后排列方法 * @param row */ public static void play(int row) { //遍历当前行的所有单元格 for (int i = 0; i < 8; i++) { //判断本格是否可以放皇后 if(check(row,i)) { map[row][i] = 1; //判断是否为最后一行 if(row == 7) { show(); }else { //接着走下一步 play(row + 1); } //取消当前落子 清空棋盘 map[row][i] = 0; } } } public static void main(String[] args) { play(0); } }