之前做的一个项目,现在才把它记录下来,项目中有一个这样功能,用户在地图上面选择起点、终点,需要在地图上面寻找一条最短的路径并显示到地图上。
在寻路中有两个比较常用的算法,一个迪杰斯特(Dijkstra)算法,迪杰斯特拉(Dijkstra)算法是典型最短路径算法,用于计算一个节点到其他节点的最短路径。 它的主要特点是以起始点为中心向外层层扩展(广度优先搜索思想),直到扩展到终点为止。因为它是一个广度优先的算法,所以它的效率会比较低。另外一个是A星算法,它是基于启发式代价函数,启发式函数在应用和游戏中非常有用。在速度和精确度之间取得折中将会让你的程序运行得更快。
下面是我实现的代码:
package com.heng.test.map; import java.util.*; /** * A*算法 * @author 黎荣恒 * */ public class AStar { private int[][] map;// 地图(0可通过 1不可通过) private List<Node> openList;// 开启列表 private List<Node> closeList;// 关闭列表 private final int COST_STRAIGHT = 10;// 垂直方向或水平方向移动的路径评分 private final int COST_DIAGONAL = 14;// 斜方向移动的路径评分 private int row;// 行 private int column;// 列 public AStar(int[][] map, int row, int column) { this.map = map; this.row = row;//行 this.column = column;//列 openList = new ArrayList<Node>();//初始化开启列表 closeList = new ArrayList<Node>();//初始化关闭列表 } /** * 查找坐标 * @param x1 起点x轴坐标 * @param y1 起点y轴坐标 * @param x2 终点x轴坐标 * @param y2 终点y轴坐标 * @return (-1:错误,0:没找到,1:找到了) */ public int search(int x1, int y1, int x2, int y2) { // 输入有错误 if (x1 < 0 || x1 >= row || x2 < 0 || x2 >= row || y1 < 0 || y1 >= column || y2 < 0 || y2 >= column) { return -1; } // 输入有错误 if (map[x1][y1] == 1 || map[x2][y2] == 1) { return -1; } Node sNode = new Node(x1, y1, null);//起点 Node eNode = new Node(x2, y2, null);//终点 openList.add(sNode); List<Node> resultList = search(sNode, eNode); if (resultList.size() == 0) { return 0; } for (Node node : resultList) { map[node.getX()][node.getY()] = 2; } return 1; } /** * 查找核心算法 * @param sNode 起点 * @param eNode 终点 * @return 可走路径做点队列 */ private List<Node> search(Node sNode, Node eNode) { List<Node> resultList = new ArrayList<Node>(); boolean isFind = false; Node node = null; while (openList.size() > 0) { // System.out.println(openList); // 取出开启列表中最低F值,即第一个存储的值的F为最低的 node = openList.get(0); // 判断是否找到目标点 if (node.getX() == eNode.getX() && node.getY() == eNode.getY()) { isFind = true; System.out.println("------找到目标------"); break; } // 上 if ((node.getY() - 1) >= 0) { checkPath(node.getX(), node.getY() - 1, node, eNode, COST_STRAIGHT); } // 下 if ((node.getY() + 1) < column) { checkPath(node.getX(), node.getY() + 1, node, eNode, COST_STRAIGHT); } // 左 if ((node.getX() - 1) >= 0) { checkPath(node.getX() - 1, node.getY(), node, eNode, COST_STRAIGHT); } // 右 if ((node.getX() + 1) < row) { checkPath(node.getX() + 1, node.getY(), node, eNode, COST_STRAIGHT); } // 左上 if ((node.getX() - 1) >= 0 && (node.getY() - 1) >= 0) { checkPath(node.getX() - 1, node.getY() - 1, node, eNode, COST_DIAGONAL); } // 左下 if ((node.getX() - 1) >= 0 && (node.getY() + 1) < column) { checkPath(node.getX() - 1, node.getY() + 1, node, eNode, COST_DIAGONAL); } // 右上 if ((node.getX() + 1) < row && (node.getY() - 1) >= 0) { checkPath(node.getX() + 1, node.getY() - 1, node, eNode, COST_DIAGONAL); } // 右下 if ((node.getX() + 1) < row && (node.getY() + 1) < column) { checkPath(node.getX() + 1, node.getY() + 1, node, eNode, COST_DIAGONAL); } // 从开启列表中删除 // 添加到关闭列表中 closeList.add(openList.remove(0)); // 开启列表中排序,把F值最低的放到最底端 Collections.sort(openList, new NodeFComparator()); // System.out.println(openList); } if (isFind) { System.out.println("node = "+node.getParentNode()); getPath(resultList, node); } return resultList; } /** * 查询此路是否能走通 * @param x * @param y * @param parentNode * @param eNode * @param cost * @return */ private boolean checkPath(int x, int y, Node parentNode, Node eNode, int cost) { //System.out.println("--------checkPath-------"); Node node = new Node(x, y, parentNode); // 查找地图中是否能通过,不能通过则把该点加入到关闭列表 if (map[x][y] == 1) { closeList.add(node); return false; } // 查找关闭列表中是否存在 if (isListContains(closeList, x, y) != -1) { return false; } // 查找开启列表中是否存在 int index = -1; if ((index = isListContains(openList, x, y)) != -1) { // G值是否更小,即是否更新G,F值 if ((parentNode.getG() + cost) < openList.get(index).getG()) { node.setParentNode(parentNode); countG(node, eNode, cost); countF(node); openList.set(index, node); } } else { // 添加到开启列表中 node.setParentNode(parentNode); count(node, eNode, cost); openList.add(node); } return true; } // 集合中是否包含某个元素(-1:没有找到,否则返回所在的索引) private int isListContains(List<Node> list, int x, int y) { //System.out.println("----------寻路中-------"); for (int i = 0; i < list.size(); i++) { Node node = list.get(i); if (node.getX() == x && node.getY() == y) { return i; } } return -1; } // 从终点往返回到起点 private void getPath(List<Node> resultList, Node node) { System.out.println("node = "+node); if (node.getParentNode() != null) { getPath(resultList, node.getParentNode()); } resultList.add(node); } // 计算G,H,F值 private void count(Node node, Node eNode, int cost) { countG(node, eNode, cost); countH(node, eNode); countF(node); } // 计算G值 private void countG(Node node, Node eNode, int cost) { if (node.getParentNode() == null) { node.setG(cost); } else { node.setG(node.getParentNode().getG() + cost); } } // 计算H值 private void countH(Node node, Node eNode) { node.setF((Math.abs(node.getX() - eNode.getX()) + Math.abs(node.getY() - eNode.getY())) * 10); } // 计算F值 private void countF(Node node) { node.setF(node.getG() + node.getH()); } } // 节点比较类 class NodeFComparator implements Comparator<Node> { @Override public int compare(Node o1, Node o2) { return o1.getF() - o2.getF(); } }
案例代码下载:http://download.csdn.net/detail/u013043346/9327155