天天看点

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

一、深度优先搜索 

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

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

二、    重排九宫问题游戏

在一个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);

  }

   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);

  }

   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 ;

  }

   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 ;

    }

     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 ;

      }

    }

     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>

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

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

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