很多问题都可以归结为图的遍历,但这些问题中的图却不是事先给定、从程序中读入的,而是由程序动态生成的,称为隐式图。
回溯法一般是要找一个(或者所有)满足约束的解(或者某种意义下的最优解)。
路径搜索问题可以归结为隐式图的遍历,它的任务是找到一条从初始状态到终止状态的(最优)路径。
BFS是图的广度遍历,借助队列来实现。(其实觉得图真的蛮重要,递归可以视作解答树的深度遍历这里又出现了广度遍历。)
经典问题一:迷宫问题
n*n的格子,0为路,1为墙,找出可以通关的最短路径长度
计算每一个点到起点的最短距离。如果需要路径的话就要记录它的前一个结点。
class Point{
int x;
int y;
int d;
public Point(int x, int y, int d) {
this.x = x;
this.y = y;
this.d = d;
}
}
public static void main(String[] args) {
Scanner scn=new Scanner(System.in);
Point[] points=new Point[4];
points[0]=new Point(1,0,0);
points[1]=new Point(0,1,0);
points[2]=new Point(-1,0,0);
points[3]=new Point(0,-1,0);
int n=scn.nextInt();
int[][] a=new int[n][n];
int[][]dist=new int[n][n];
for(int i=0;i<n;i++) {
for(int j=0;j<n;j++) {
a[i][j]=scn.nextInt();
dist[i][j]=-1;
}
}
Queue<Point> list=new LinkedList<Point>();
list.add(new Point(0,0,0));
while(!list.isEmpty()) {
Point p=list.poll();
for(int i=0;i<4;i++) {
int newX=p.x+points[i].x;
int newY=p.y+points[i].y;
if(newX>=0&&newX<n&&newY>=0&&newY<n) {//合法
if(a[newX][newY]==0) {
if(dist[newX][newY]==-1) {
dist[newX][newY]=p.d+1;list.add(new Point(newX,newY,p.d+1));
}else if(dist[newX][newY]>p.d+1) {
dist[newX][newY]=p.d+1;
list.add(new Point(newX,newY,p.d+1));
}
}
}
}
}
System.out.println(dist[0][n-1]);
}
经典问题二:八数码问题
3*3的格子,有一个空,从初始目标移动到最终目标,求最短移动步数
class State{
int[] b=new int[9];
int dist=0;
public State getEquals() {
int[]temp=new int[9];
for(int i=0;i<9;i++) {
temp[i]=b[i];
}
return new State(temp);
}
public State(int[] b) {
super();
this.b = b;
}
public boolean isEquals(State s) {
for(int i=0;i<9;i++) {
if(b[i]!=s.b[i])
return false;
}
return true;
}
public int find0() {
for(int i=0;i<9;i++) {
if(b[i]==0)
return i;
}
return -1;
}
}
public static void main(String[] args) {
int[] end= {8,1,5,7,3,6,4,2,0};
State goal= new State(end);
Queue<State> list=new LinkedList<State>();
int[] dx= {1,-1,0,0};
int[] dy= {0,0,1,-1};
int[] start={8,1,5,7,3,6,4,0,2};
list.add(new State(start));
while(!list.isEmpty()) {
State s=list.poll();
if(s.equals(goal)) {
System.out.println(s.dist);
return;
}
int z=s.find0();
int x=z/3,y=z%3;
for(int i=0;i<4;i++) {
int newX=x+dx[i];
int newY=y+dy[i];
int newZ=3*newX+newY;
if(newX>=0&&newX<3&&newY>=0&&newY<3) {
//换位置
State st=s.getEquals();
st.b[z]=st.b[newZ];
st.b[newZ]=0;
st.dist=s.dist+1;
list.add(st);
}
}
}
System.out.println("-1");
return;
}
按书上给的翻译的java代码,目前这个思路我觉得是有点问题的因为在队列的这个移动会无限膨胀而不知道有没有解,可能最优解在队列里但是永远轮不到它。所以这里的队列应该是一个去重队列,出现过的不能重复出现。(BFS的判重操作)
判重:是为了避免同一个节点被重复访问多次。在树中,不需要判重因为不可能同一个节点访问两次。但是在图中,这就是非常必要的了,不然不仅会引起极大的时间和空间上的浪费,还可能无限膨胀。