深度优先搜索和广度优先搜索

一、深度优先搜索 
    深度优先搜索就是在搜索树的每一层始终先只扩展一个子节点,不断地向纵深前进直到不能再前进(到达叶子节点或受到深度限制)时,才从当前节点返回到上一级节点,沿另一方向又继续前进。这种方法的搜索树是从树根开始一枝一枝逐渐形成的。

      深度优先搜索亦称为纵向搜索。由于一个有解的问题树可能含有无穷分枝,深度优先搜索如果误入无穷分枝(即深度无限),则不可能找到目标节点。所以,深度优先搜索策略是不完备的。另外,应用此策略得到的解不一定是最佳解(最短路径)。

二、    重排九宫问题游戏
在一个3乘3的九宫中有1-8的8个数及一个空格随机摆放在其中的格子里。如下面左图所示。现在要求实现这样的问题:将该九宫调整为如下图右图所示的形式。调整规则是:每次只能将与空格(上,下或左,右)相临的一个数字平移到空格中。试编程实现。

| 2 | 8  | 3 |                 | 1 | 2 | 3 |
-
| 1 |     | 4 |                 | 8 |    | 4 |

| 7 | 6  | 5 |                 | 7 | 6 | 5 |

深度优先搜索的路径示意图:
 

三、广度优先搜索

    在深度优先搜索算法中,是深度越大的结点越先得到扩展。如果在搜索中把算法改为按结点的层次进行搜索, 本层的结点没有搜索处理完时,不能对下层结点进行处理,即深度越小的结点越先得到扩展,也就是说先产生 的结点先得以扩展处理,这种搜索算法称为广度优先搜索法。

广度优先搜索路径示意图:

四、航班问题(来自《The Art of Java》)
    一位顾客要预定一张从New York到Los Angeles的航班机票,下面是航班线路,请你为顾客找一种购票方案。

航班
距离
New York到Chicago
900英里
Chicago到Denver
1000英里
New York到Toronto
500英里
New York到Denver
1800英里
Toronto到Calgary
1700英里
Toronto到Los Angeles
2500英里
Toronto到Chicago
500英里
Denver到Urbana
1000英里
Denver到Houston
1000英里
Houston到Los Angeles
1500英里
Denver到Los Angeles
1000英里

下面是用深度优先搜索求解的程序:

//  Find connections using a depth-first search.
import  java.util. * ;
import  java.io. * ;

//  Flight information.
class  FlightInfo {
  String from;
  String to;
  
int  distance;
  
boolean  skip;  //  used in backtracking

  FlightInfo(String f, String t, 
int  d) {
    from 
=  f;
    to 
=  t;
    distance 
=  d;
    skip 
=   false ;
  }
}

class  Depth {
  
final   int  MAX  =   100 ;

  
//  This array holds the flight information.
  FlightInfo flights[]  =   new  FlightInfo[MAX]; 

  
int  numFlights  =   0 //  number of entries in flight array

  Stack btStack 
=   new  Stack();  //  backtrack stack

  
public   static   void  main(String args[])
  {
    
    String to, from;
    Depth ob 
=   new  Depth();
    BufferedReader br 
=   new  
      BufferedReader(
new  InputStreamReader(System.in)); 
 
    ob.setup();  

    
try  { 
      System.out.print(
" From?  " );
      from 
=  br.readLine(); 
      System.out.print(
" To?  " );
      to 
=  br.readLine(); 

      ob.isflight(from, to);

      
if (ob.btStack.size()  !=   0 )
        ob.route(to);
    } 
catch  (IOException exc) { 
      System.out.println(
" Error on input. " );
    }
  }
  
  
//  Initialize the flight database.
   void  setup()
  {
    addFlight(
" New York " " Chicago " 900 );
    addFlight(
" Chicago " " Denver " 1000 );
    addFlight(
" New York " " Toronto " 500 );
    addFlight(
" New York " " Denver " 1800 );
    addFlight(
" Toronto " " Calgary " 1700 );
    addFlight(
" Toronto " " Los Angeles " 2500 );
    addFlight(
" Toronto " " Chicago " 500 );
    addFlight(
" Denver " " Urbana " 1000 );
    addFlight(
" Denver " " Houston " 1000 );
    addFlight(
" Houston " " Los Angeles " 1500 );
    addFlight(
" Denver " " Los Angeles " 1000 );
  }
  
  
//  Put flights into the database.
   void  addFlight(String from, String to,  int  dist)
  {
  
    
if (numFlights  <  MAX) {
      flights[numFlights] 
=
        
new  FlightInfo(from, to, dist);

      numFlights
++ ;
    }
    
else  System.out.println( " Flight database full. " );
  }

  
//  Show the route and total distance.
   void  route(String to)
  {
    Stack rev 
=   new  Stack();
    
int  dist  =   0 ;
    FlightInfo f;
    
int  num  =  btStack.size();

    
//  Reverse the stack to display route.
     for ( int  i = 0 ; i  <  num; i ++
      rev.push(btStack.pop());

    
for ( int  i = 0 ; i  <  num; i ++ ) {
      f 
=  (FlightInfo) rev.pop();
      System.out.print(f.from 
+   "  to  " );
      dist 
+=  f.distance;
    }

    System.out.println(to);
    System.out.println(
" Distance is  "   +  dist);
  }

  
/*  If there is a flight between from and to,
     return the distance of flight;
     otherwise, return 0. 
*/
  
int  match(String from, String to)
  {
    
for ( int  i = numFlights - 1 ; i  >   - 1 ; i -- ) {
      
if (flights[i].from.equals(from)  &&
         flights[i].to.equals(to) 
&&
         
! flights[i].skip)
      {
        flights[i].skip 
=   true //  prevent reuse
         return  flights[i].distance;
      }
    }

    
return   0 //  not found 
  }
  
  
//  Given from, find any connection.
  FlightInfo find(String from)
  {

    
for ( int  i = 0 ; i  <  numFlights; i ++ ) {
      
if (flights[i].from.equals(from)  &&
         
! flights[i].skip)
      {
        FlightInfo f 
=   new  FlightInfo(flights[i].from,
                             flights[i].to,
                             flights[i].distance);
        flights[i].skip 
=   true //  prevent reuse

        
return  f;
      }
    }

    
return   null ;
  }
  
  
//  Determine if there is a route between from and to. 
   void  isflight(String from, String to)
  {
    
int  dist;
    FlightInfo f;

    
//  See if at destination.
    dist  =  match(from, to);
    
if (dist  !=   0 ) {
      btStack.push(
new  FlightInfo(from, to, dist));
      
return ;
    }

    
//  Try another connection.
    f  =  find(from);
    
if (f  !=   null ) {
      btStack.push(
new  FlightInfo(from, to, f.distance));
      isflight(f.to, to);
    }
    
else   if (btStack.size()  >   0 ) {
      
//  Backtrack and try another connection.
      f  =  (FlightInfo) btStack.pop();
      isflight(f.from, f.to);
    }
  }
}  

 

解释:isflight()方法用递归方法进行深度优先搜索,它先调用match()方法检查航班的数据库,判断在from和to之间有没有航班可达。如果有,则获取目标信息,并将该线路压入栈中,然后返回(找到一个方案)。否则,就调用find()方法查找from与任意其它城市之间的线路,如果找到一条就返回描述该线路的FlightInfo对象,否则返回null。如果存在这样的一条线路,那么就把该线路保存在f中,并将当前航班信息压到栈的顶部,然后递归调用isflight()方法 ,此时保存在f.to中的城市成为新的出发城市.否则就进行回退,弹出栈顶的第一个节点,然后递归调用isflight()方法。该过程将一直持续到找到目标为止。

 

程序运行结果:


C:/java>java Depth
From? New York
To? Los Angeles
New York to Chicago to Denver to Los Angeles
Distance is 2900

C:/java>

     深度优先搜索能够找到一个解,同时,对于上面这个特定问题,深度优先搜索没有经过回退,一次就找到了一个解;但如果数据的组织方式不同,寻找解时就有可能进行多次回退。因此这个例子的输出并不具有普遍性。而且,在搜索一个很长,但是其中并没有解的分支的时候,深度优先搜索的性能将会很差,在这种情况下,深度优先搜索不仅在搜索这条路径时浪费时间,而且还在向目标的回退中浪费时间。

再看对这个例子使用广度优先搜索的程序:

程序运行结果:

C:/java>java Breadth
From? New York
To? Los Angeles
New York to Toronto to Los Angeles
Distance is 3000

//  Find connections using a breadth-first search.
import  java.util. * ;
import  java.io. * ;

//  Flight information.
class  FlightInfo {
  String from;
  String to;
  
int  distance;
  
boolean  skip;  //  used in backtracking

  FlightInfo(String f, String t, 
int  d) {
    from 
=  f;
    to 
=  t;
    distance 
=  d;
    skip 
=   false ;
  }
}

class  Breadth {
  
final   int  MAX  =   100 ;

  
//  This array holds the flight information.
  FlightInfo flights[]  =   new  FlightInfo[MAX]; 

  
int  numFlights  =   0 //  number of entries in flight array

  Stack btStack 
=   new  Stack();  //  backtrack stack

  
public   static   void  main(String args[])
  {
    String to, from;
    Breadth ob 
=   new  Breadth();
    BufferedReader br 
=   new  
      BufferedReader(
new  InputStreamReader(System.in)); 
 
    ob.setup();  

    
try  { 
      System.out.print(
" From?  " );
      from 
=  br.readLine(); 
      System.out.print(
" To?  " );
      to 
=  br.readLine(); 

      ob.isflight(from, to);

      
if (ob.btStack.size()  !=   0 )
        ob.route(to);
    } 
catch  (IOException exc) { 
      System.out.println(
" Error on input. " );
    }
  }
  
  
//  Initialize the flight database.
   void  setup()
  {
    addFlight(
" New York " " Chicago " 900 );
    addFlight(
" Chicago " " Denver " 1000 );
    addFlight(
" New York " " Toronto " 500 );
    addFlight(
" New York " " Denver " 1800 );
    addFlight(
" Toronto " " Calgary " 1700 );
    addFlight(
" Toronto " " Los Angeles " 2500 );
    addFlight(
" Toronto " " Chicago " 500 );
    addFlight(
" Denver " " Urbana " 1000 );
    addFlight(
" Denver " " Houston " 1000 );
    addFlight(
" Houston " " Los Angeles " 1500 );
    addFlight(
" Denver " " Los Angeles " 1000 );
  }
  
  
//  Put flights into the database.
   void  addFlight(String from, String to,  int  dist)
  {  
    
if (numFlights  <  MAX) {
      flights[numFlights] 
=
        
new  FlightInfo(from, to, dist);

      numFlights
++ ;
    }
    
else  System.out.println( " Flight database full. " );
  }

  
//  Show the route and total distance.
   void  route(String to)
  {
    Stack rev 
=   new  Stack();
    
int  dist  =   0 ;
    FlightInfo f;
    
int  num  =  btStack.size();

    
//  Reverse the stack to display route.
     for ( int  i = 0 ; i  <  num; i ++
      rev.push(btStack.pop());

    
for ( int  i = 0 ; i  <  num; i ++ ) {
      f 
=  (FlightInfo) rev.pop();
      System.out.print(f.from 
+   "  to  " );
      dist 
+=  f.distance;
    }

    System.out.println(to);
    System.out.println(
" Distance is  "   +  dist);
  }

  
/*  If there is a flight between from and to,
     return the distance of flight;
     otherwise, return 0. 
*/
  
int  match(String from, String to)
  {
    
for ( int  i = numFlights - 1 ; i  >   - 1 ; i -- ) {
      
if (flights[i].from.equals(from)  &&
         flights[i].to.equals(to) 
&&
         
! flights[i].skip)
      {
        flights[i].skip 
=   true //  prevent reuse
         return  flights[i].distance;
      }
    }

    
return   0 //  not found 
  }
  
  
//  Given from, find any connection.
  FlightInfo find(String from)
  {
    
for ( int  i = 0 ; i  <  numFlights; i ++ ) {
      
if (flights[i].from.equals(from)  &&
         
! flights[i].skip)
      {
        FlightInfo f 
=   new  FlightInfo(flights[i].from,
                             flights[i].to,
                             flights[i].distance);
        flights[i].skip 
=   true //  prevent reuse

        
return  f;
      }
    }

    
return   null ;
  }
  
  
/*  Determine if there is a route between from and to
     using breadth-first search. 
*/
  
void  isflight(String from, String to)
  {
    
int  dist, dist2;
    FlightInfo f;

    
//  This stack is needed by the breadth-first search.
    Stack resetStck  =   new  Stack();

    
//  See if at destination.
    dist  =  match(from, to);
    
if (dist  !=   0 ) {
      btStack.push(
new  FlightInfo(from, to, dist));
      
return ;
    }

    
/*  Following is the first part of the breadth-first
       modification.  It checks all connecting flights
       from a specified node. 
*/
    
while ((f  =  find(from))  !=   null ) {
      resetStck.push(f);
      
if ((dist  =  match(f.to, to))  !=   0 ) {
        resetStck.push(f.to);
        btStack.push(
new  FlightInfo(from, f.to, f.distance));
        btStack.push(
new  FlightInfo(f.to, to, dist));
        
return ;
      }
    }

    
/*  The following code resets the skip fields set by
       preceding while loop. This is also part of the
       breadth-first modifiction. 
*/
    
int  i  =  resetStck.size();
    
for (; i != 0 ; i -- )
      resetSkip((FlightInfo) resetStck.pop());

    
//  Try another connection.
    f  =  find(from);
    
if (f  !=   null ) {
      btStack.push(
new  FlightInfo(from, to, f.distance));
      isflight(f.to, to);
    }
    
else   if (btStack.size()  >   0 ) {
      
//  Backtrack and try another connection.
      f  =  (FlightInfo) btStack.pop();
      isflight(f.from, f.to);
    }
  }

  
//  Reset skip field of specified flight.
   void  resetSkip(FlightInfo f) {
    
for ( int  i = 0 ; i <  numFlights; i ++ )
      
if (flights[i].from.equals(f.from)  &&
         flights[i].to.equals(f.to))
           flights[i].skip 
=   false ;
  }
}

 

C:/java>

它找到了一个合理的解,但这不具有一般性。因为找到的第一条路径取决于信息的物理组织形式。

如果目标在搜索空间中隐藏得不是太深,那么广度优先搜索的性能会很好。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值