最近在整理搜索算法相关内容,今天来介绍一下双向搜索算法
传统的深度优先算法,通过不断寻找子状态和回溯,来达到遍历所有状态的目的,存在不少缺点。
1. 可能会陷入死循环:如果图中存在环路,深度优先算法可能会陷入死循环,无法找到解决方案。
2. 不一定能找到最优解:深度优先算法只会沿着一条路径一直走下去,直到找到解决方案或者无法继续下去为止。因此,它不能保证找到最优解,有可能会找到次优解。
3. 空间复杂度高:深度优先算法需要使用递归或者栈来保存搜索过程中的状态,因此在搜索深度较大的情况下,空间复杂度会很高,可能会导致内存溢出。
4. 可能会忽略一些解:深度优先算法只会搜索一条路径,如果存在多个解决方案,但是它们不在同一条路径上,那么深度优先算法可能会忽略其中一些解决方案。
而双向搜索算法作为深度优先搜索的一种改进方法,能够解决一些问题
双向搜索算法是一种搜索算法,它从起点和终点同时开始搜索,直到两个搜索路径相遇。这种算法通常用于搜索问题,其中起点和终点之间的距离较远,而搜索空间较大,例如在图形搜索、字符串匹配和游戏搜索中。 双向搜索算法的基本思想是从起点和终点同时开始搜索,每次从两个方向中选择一个节点进行扩展,直到两个搜索路径相遇。这种算法可以减少搜索空间,因为它同时从两个方向搜索,可以更快地找到解决方案。 双向搜索算法的优点是可以减少搜索空间,因为它同时从两个方向搜索,可以更快地找到解决方案。缺点是需要额外的空间来存储两个搜索路径,并且需要更复杂的算法来处理两个搜索路径的交叉点。 双向搜索算法的应用非常广泛,例如在图形搜索中,可以从起点和终点同时开始搜索,以找到最短路径。在字符串匹配中,可以从两个方向同时搜索,以找到匹配的字符串。在游戏搜索中,可以从当前状态和目标状态同时开始搜索,以找到最优解。
下面是双向搜索算法解决八数码问题的实例
具体代码思路:声明两个path队列分别记录由源节点和目标节点出发搜索的节点,while循环出队两者中数据量较小的队列的队首节点,进行扩展和入队。用两个rep栈为两条线路查重,如果有元素在两个查重栈中都有则两个搜索相遇,返回相遇状态。用两个hashmap对象存放两个搜索线路中子状态和父状态键值对,在搜索完成后向上查找父状态完成搜索线路的输出。extend函数负责扩展节点,使新节点完成一系列入队入栈操作,没有发现相遇节点则返回null。最后输出程序扩展节点和路径节点数,输出程序运行时间。细节请见源代码。
import Utils.Print;
import Utils.Transfer;
import java.util.*;
public class bidirectSearch {
private static Stack<String> allNode;// 记录总扩展结点数
private static Set<String> rep1;//为两个搜索线路去重
private static Set<String> rep2;
private static Deque<String> path1;//两个搜索线路
private static Deque<String> path2;
private static Map<String, String> parent1;//记录子状态和父状态键值对,记录路径
private static Map<String, String> parent2;
private static String initState;
private static String finalState;
public static void main(String[] args) {
rep1 = new HashSet();
rep2 = new HashSet<>();
path1 = new LinkedList<>();
path2 = new LinkedList<>();
allNode = new Stack<>();
parent1 = new HashMap<>();
parent2 = new HashMap<>();
Scanner scan = new Scanner(System.in);
System.out.print("开始状态是:");
initState = scan.nextLine();
System.out.print("目标状态是:");
finalState = scan.nextLine();
//毫秒ms:
long startTime=System.currentTimeMillis(); //获取开始时间
path1.offerLast(initState);
path2.offerLast(finalState);
rep1.add(initState);
rep2.add(finalState);
allNode.push(initState);
allNode.push(finalState);
parent1.put(initState, "初始状态");
parent2.put(finalState, "结束状态");
String isMeet = finalState;
while(!path1.isEmpty() && !path2.isEmpty()){
String curr;
boolean isfirst = true;
if(path1.size() > path2.size()){ //选择数量小的搜索路径扩展
isfirst = false;
curr = path2.pollLast();
}else{
curr = path1.pollLast();
}
isMeet = extend(curr, isfirst); // 扩展结点并检查有无相遇
if(isMeet != null){
break;
}
}
System.out.println("相遇状态到源状态路径:");
Print.formPrint(isMeet);
int cnt1 = 1;
String pre = parent1.get(isMeet);
while(pre != "初始状态"){
System.out.println("上一状态为:");
Print.formPrint(pre);
pre = parent1.get(pre);
cnt1++;
}
System.out.println("相遇状态到目标状态路径:");
Print.formPrint(isMeet);
int cnt2 = 1;
pre = parent2.get(isMeet);
while(pre != "结束状态"){
System.out.println("上一状态为:");
Print.formPrint(pre);
pre = parent2.get(pre);
cnt2++;
}
System.out.println("路径结点个数:" + (cnt1 + cnt2 - 3));
System.out.println("扩展结点总个数:" + allNode.size());
long endTime=System.currentTimeMillis(); //获取结束时间
System.out.println("程序运行时间: "+(endTime-startTime)+"ms");
}
public static String extend(String state, boolean isfirst){
Deque<String> path;
Set<String> rep;
Map<String, String> parent;
if(isfirst){ // 根据isfirst判断是哪一条搜索路径,替换变量
rep = rep1;
path = path1;
parent = parent1;
}else{
rep = rep2;
path = path2;
parent = parent2;
}
String next = Transfer.upTransfer(state);//模拟八数码空格向上移函数,读者自己实现
if (next != null && !rep.contains(next)) {
path.offerLast(next); // 扩展结点入路径
rep.add(next); // 扩展结点入去重的栈
allNode.push(next);
parent.put(next, state);// 记录路径
}
if(rep1.contains(next) && rep2.contains(next)){ // 判断两个去重的栈中是否都有这个结点,若有就相遇返回。
return next;
}
next = Transfer.rightTransfer(state);
if (next != null && !rep.contains(next)) { // 同理如上
path.offerLast(next);
rep.add(next);
parent.put(next, state);
allNode.push(next);
}
if(rep1.contains(next) && rep2.contains(next)){
return next;
}
next = Transfer.downTransfer(state);
if (next != null && !rep.contains(next)) {
path.offerLast(next);
allNode.push(next);
parent.put(next, state);
rep.add(next);
}
if(rep1.contains(next) && rep2.contains(next)){
return next;
}
next = Transfer.leftTransfer(state);
if (next != null && !rep.contains(next)) {
allNode.push(next);
path.offer(next);
parent.put(next, state);
rep.add(next);
}
if(rep1.contains(next) && rep2.contains(next)){
return next;
}
return null;
}
}