双向搜索算法(基于深度优先搜索算法的优化)

最近在整理搜索算法相关内容,今天来介绍一下双向搜索算法

传统的深度优先算法,通过不断寻找子状态和回溯,来达到遍历所有状态的目的,存在不少缺点。

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

​

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
由于题目不确定细节,以下是我自己实现的简单版本,供参考: 1. 广度优先算法(BFS) ```c #include <stdio.h> #include <stdlib.h> #define MAX_N 100 #define INF 1e9 int n, m; /* n: 点数,m: 边数 */ int graph[MAX_N][MAX_N]; /* 邻接矩阵 */ int dist[MAX_N]; /* 距离数组 */ int visited[MAX_N]; /* 是否访问过 */ int queue[MAX_N]; /* 队列 */ int front = 0, rear = -1; void bfs(int start) { for (int i = 0; i < n; ++i) { dist[i] = INF; /* 初始化距离数组 */ visited[i] = 0; } dist[start] = 0; /* 起点距离为0 */ visited[start] = 1; queue[++rear] = start; /* 入队 */ while (front <= rear) { /* 队列非空 */ int cur = queue[front++]; /* 出队 */ for (int i = 0; i < n; ++i) { if (graph[cur][i] && !visited[i]) { /* 如果存在边且未访问过 */ dist[i] = dist[cur] + 1; /* 更新距离 */ visited[i] = 1; queue[++rear] = i; /* 入队 */ } } } } int main() { scanf("%d %d", &n, &m); for (int i = 0; i < m; ++i) { int u, v; scanf("%d %d", &u, &v); graph[u][v] = 1; /* 无向图,建双向边 */ graph[v][u] = 1; } bfs(0); for (int i = 0; i < n; ++i) { printf("%d ", dist[i]); } printf("\n"); return 0; } ``` 2. 深度优先算法(DFS) ```c #include <stdio.h> #include <stdlib.h> #define MAX_N 100 int n, m; int graph[MAX_N][MAX_N]; /* 邻接矩阵 */ int visited[MAX_N]; /* 是否访问过 */ void dfs(int cur) { printf("%d ", cur); /* 访问当前节点 */ visited[cur] = 1; for (int i = 0; i < n; ++i) { if (graph[cur][i] && !visited[i]) { /* 如果存在边且未访问过 */ dfs(i); /* 递归深搜 */ } } } int main() { scanf("%d %d", &n, &m); for (int i = 0; i < m; ++i) { int u, v; scanf("%d %d", &u, &v); graph[u][v] = 1; /* 无向图,建双向边 */ graph[v][u] = 1; } dfs(0); return 0; } ``` 3. A*算法 ```c #include <stdio.h> #include <stdlib.h> #include <math.h> #define MAX_N 100 #define INF 1e9 typedef struct { int x; int y; } Point; int n, m; Point points[MAX_N]; /* 点数组 */ int graph[MAX_N][MAX_N]; /* 邻接矩阵 */ int dist[MAX_N]; /* 距离数组 */ int visited[MAX_N]; /* 是否访问过 */ /* 计算两个点之间的欧几里得距离 */ double dist_point(Point p1, Point p2) { double dx = (double) (p1.x - p2.x); double dy = (double) (p1.y - p2.y); return sqrt(dx * dx + dy * dy); } /* 启发函数,估计点x到点y的距离 */ double heuristic(int x, int y) { return dist_point(points[x], points[y]); } /* 优先队列实现(小根堆) */ typedef struct { int n; /* 当前节点数量 */ int a[MAX_N]; /* 节点数组 */ double f[MAX_N]; /* 估价函数值 */ } Heap; void heap_init(Heap *h) { h->n = 0; } void heap_push(Heap *h, int x, double f) { int i = ++h->n; while (i > 1 && f < h->f[i / 2]) { /* 上浮操作 */ h->a[i] = h->a[i / 2]; h->f[i] = h->f[i / 2]; i /= 2; } h->a[i] = x; h->f[i] = f; } int heap_pop(Heap *h) { int min = h->a[1]; int x = h->a[h->n--]; int i = 1, j = 2; while (j <= h->n) { /* 下沉操作 */ if (j < h->n && h->f[j] > h->f[j + 1]) { j++; } if (h->f[j] >= h->f[i]) { break; } h->a[i] = h->a[j]; h->f[i] = h->f[j]; i = j; j *= 2; } h->a[i] = x; return min; } int heap_empty(Heap *h) { return h->n == 0; } void a_star(int start, int end) { for (int i = 0; i < n; ++i) { dist[i] = INF; /* 初始化距离数组 */ visited[i] = 0; } Heap heap; heap_init(&heap); dist[start] = 0; /* 起点距离为0 */ heap_push(&heap, start, heuristic(start, end)); /* 将起点加入队列 */ while (!heap_empty(&heap)) { /* 队列非空 */ int cur = heap_pop(&heap); /* 取出优先级最高的节点 */ visited[cur] = 1; if (cur == end) { /* 到达终点 */ return; } for (int i = 0; i < n; ++i) { if (graph[cur][i] && !visited[i]) { /* 如果存在边且未访问过 */ double new_dist = dist[cur] + dist_point(points[cur], points[i]); if (new_dist < dist[i]) { dist[i] = new_dist; heap_push(&heap, i, dist[i] + heuristic(i, end)); /* 加入队列 */ } } } } } int main() { scanf("%d %d", &n, &m); for (int i = 0; i < n; ++i) { int x, y; scanf("%d %d", &x, &y); points[i].x = x; points[i].y = y; } for (int i = 0; i < m; ++i) { int u, v; scanf("%d %d", &u, &v); graph[u][v] = 1; /* 无向图,建双向边 */ graph[v][u] = 1; } a_star(0, n - 1); printf("%d\n", dist[n - 1]); return 0; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值