面试题中经常会遇到,给定一个0,1矩阵,0表示可走,1表示不可走。求出从左上角到右下角的最短路径?
例如:矩阵A={
{0,0,1,0},
{1,0,1,0},
{0,0,1,0},
{0,0,0,0}
};
这里我们就可以用广度优先算法来实现:
import java.util.LinkedList; public class MiGong { public static void main(String[] args) { int[][] array={ {0,0,1,0}, {1,0,1,0}, {0,0,1,0}, {0,0,0,0} }; new MiGong().findMinRold(array); } //定义图的节点信息 class Node{ //定义坐标和距离第一个节点的距离 int x; int y; int dis; //定义节点的前缀,用于绘制整个最短路径的 线路图 Node pre; public Node(int x , int y , int dis , Node pre) { this.x = x; this.y = y; this.dis = dis; this.pre = pre; } } public void findMinRold(int[][] arr) { //定义 上下左右四个方向 int[][] direction = {{1,0},{-1,0},{0,1},{0,-1}}; //创建队列 LinkedList<Node> queue = new LinkedList<Node>(); //创建开始节点 Node start = new Node(0,0,0,null); //把开始节点放入队列中,并做下标记,在原来数组中做标记 queue.offer(start); arr[0][0] = 1; //循环操作队列,进行广度遍历 Node temp = null; ok: while(!queue.isEmpty()) { temp = queue.poll(); //依次遍历这个节点的四个方向,查找还没有遍历的相连节点 for(int i = 0 ; i < 4 ; i++) { int newX = temp.x + direction[i][0]; int newY = temp.y + direction[i][1]; //是否越界,坐标 if(newX < 0 || newX >= 4 || newY < 0 || newY >= 4) { continue; } //判断是否该节点可以通过,即为0 if(arr[newX][newY] == 1) { continue; } //构造节点 Node next = new Node(newX , newY , temp.dis+1 , temp); //该节点可以通过,判断该节点是否是最终节点 if(newX == 3 && newY == 3) { queue.clear(); //相当于头插法,转置 queue.offerFirst(next); Node preNode = next.pre; while(preNode != null) { queue.offerFirst(preNode); preNode = preNode.pre; } int len = queue.size(); System.out.println("最短路径长度为:"+(len-1)); for(Node it:queue) { System.out.println("("+it.x+","+it.y+") "); } break ok; } arr[newX][newY] = 1; queue.offer(next); }//for }//while } }
附录:BFS求解第一个节点到其他节点之间的最短路径问题:
import java.util.LinkedList; import java.util.Map; import java.util.TreeMap; public class BFS { public static void main(String[] args) { int[][] arr = {{1,2,3,4},{5,6,7,8},{9,10,11,12},{13,14,15,16}}; int[][] mark = new int[4][4]; new BFS().BFS(arr, mark); } //构造节点,包括x和y、以及与第一个节点的距离 class Node{ int x; int y; int dis; public Node(int x , int y , int dis) { this.x = x ; this.y = y ; this.dis = dis ; } } // 1:使用队列LinkedList 2:使用标记数组mark和坐标 public void BFS(int[][] arr , int[][] mark) { //记录每个节点到第一个节点的最短距离 Map<Integer,Integer> map = new TreeMap<Integer,Integer>(); //上下左右四个方向 int[][] derect = {{1,0},{-1,0},{0,1},{0,-1}}; //建立一个队列 LinkedList<Node> queue = new LinkedList<Node>(); //初始化第一个节点 Node start = new Node(0,0,0); //把第一个节点放到队列中去 queue.offer(start); //置该点标记为为1,表示已经访问过 mark[0][0] = 1; //第一个节点到自己的距离 map.put(1, 0); while(!queue.isEmpty()) { //出队列 Node temp = queue.poll(); //将其相关联的节点找出来,并依次进入队列 for(int i = 0 ; i < 4 ; i++) { //寻找相连节点 int newX = temp.x + derect[i][0]; int newY = temp.y + derect[i][1]; //新的坐标越界了,继续下一次循环 if(newX < 0 || newY < 0 || newX >= 4 || newY >= 4) { continue; } //该节点的标记mark为1,已经访问过了,则不需要进队列了 if(mark[newX][newY] == 1) { continue; } //已经找到相连节点,构建该节点,并入队列,标记为1 Node next = new Node(newX , newY , temp.dis+1); //记录第1个节点到next节点的距离 map.put(arr[newX][newY], temp.dis+1); mark[newX][newY] = 1; queue.offer(next); }//for }//while //输出节点到1节点的距离 for(Map.Entry<Integer, Integer> it:map.entrySet()) { System.out.println("1->"+it.getKey()+":"+it.getValue()); } } }