LeetCode笔记---双向搜索入门之BFS

更多看这里吧😁

请添加图片描述
无聊打发时间的时候就学了学一些LeetCode的题,以后也可以说知道这个东西啦

双向BFS

  当用BFS算法搜索可行解的时候,可能会在深层子数上花费较多的时间,比如多叉树随着层数的增长,子树节点个数将以指数级别增长。
  如果已经搜索的初态终态,可以从初态和终态分别同时进行BFS搜索

左图是传统BFS搜索的空间状态,右图是双向搜索的空间状态
在这里插入图片描述
当采用传统BFS搜索到可行解的时候已经搜索了非常多的子树;而采用双向BFS搜索到可行解的时候相比于前者少了很多搜索空间

入门例题

190. 字串变换

在这里插入图片描述
如题,由于DFS搜索的深度未知,不太适合用普通的DFS(可能stackoverflow);因为可以同时知道搜索的初态(字符串A)和搜索的终态(字符串B)。可以采用双向BFS进行优化BFS。

代码比较简单,主要就是用两个队列模拟A -> B正向BFS搜索以及B-> A反向BFS搜索的过程,当变换后的A’ == B’ ,说明二者相遇,输出步数之和即可。

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashSet;
import java.util.Scanner;

public class Main {

    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        String[] sp = scan.nextLine().split(" ");
        // 读入字符串A和B
        String a = sp[0], b = sp[1];
        String[] ra = new String[6], rb = new String[6];
        int len = 0;
        while (scan.hasNextLine()) {
            sp = scan.nextLine().split(" ");
            // 依次读入转换规格ra[]和rb[]
            ra[len] = sp[0];
            rb[len++] = sp[1];
        }
        
        // 特别判断一下,如果一开始就相等,就不用再搜索,否则会搜索不到结果
        if (a.equals(b)) {
            System.out.println(0);
            return;
        }
        int cnta = 0, cntb = 0;
        // seta和setb标记已经qa和qb两个队列搜索过的节点
        HashSet<String> seta = new HashSet<>(), setb = new HashSet<>();
        seta.add(a);
        setb.add(b);
        // 设置两个BFS搜索的队列
        Deque<String> qa = new ArrayDeque<>(), qb = new ArrayDeque<>();
        qa.addLast(a);
        qb.addLast(b);
        while (!qa.isEmpty() && !qb.isEmpty()) {
        	// 每次优先遍历少的队列
            if (qa.size() < qb.size()) {
            	// 正向搜索
                int sa = qa.size();
                for (int tt = 0; tt < sa; tt++) {
                    String top = qa.removeFirst();
                    // 对于字符串top,遍历所有的转换规则
                    for (int i = 0; i < len; i++) {
                        for (int j = 0; j < top.length(); j++) {
                            int k = 0, jj = j;
                            // 对于每种规则,判断该规则是否是top的字串,是的话替换生成新的子节点
                            for (k = 0; k < ra[i].length() && jj < top.length(); k++, jj++) {
                                if (ra[i].charAt(k) != top.charAt(jj)) break;
                            }
                            if (k == ra[i].length() && ra[i].length() <= top.length()) {							
                            	// 按照规则替换后的子节点串ne
                                String ne = top.substring(0, j) + rb[i] 
                                + top.substring(j + ra[i].length(), top.length());
                                // 如果qa队列中已经遍历过ne了,跳过ne
                                if (seta.contains(ne)) continue;
                                // 如果ne在qa(另一个搜索方向)中已经遍历过了,判断两个方向的步数是不是满足条件
                                if (setb.contains(ne)) {
                                    if (cnta + cntb + 1 <= 10) {
                                        System.out.println(cnta + cntb + 1);
                                        return;
                                    } else {
                                        continue;
                                    }
                                }
                                seta.add(ne);
                                qa.addLast(ne);
                            }
                        }
                    }
                }
                // 正向搜索的步数+1
                cnta++;
            } else {
            	// 反向搜索
           		// qb的遍历过程和qa的一样,可以抽取成一个函数,为了展示就这样了
                int sb = qb.size();
                for (int tt = 0; tt < sb; tt++) {
                    String top = qb.removeFirst();
                    for (int i = 0; i < len; i++) {
                        for (int j = 0; j < top.length(); j++) {
                            int k = 0, jj = j;
                            for (k = 0; k < rb[i].length() && jj < top.length(); k++, jj++) {
                                if (rb[i].charAt(k) != top.charAt(jj)) break;
                            }
                            if (k == rb[i].length() && rb[i].length() <= top.length()) {
                                String ne = top.substring(0, j) + ra[i] 
                                + top.substring(j + rb[i].length(), top.length());
                                if (setb.contains(ne)) continue;
                                if (seta.contains(ne)) {
                                    if (cnta + cntb + 1 <= 10) {
                                        System.out.println(cnta + cntb + 1);
                                        return;
                                    } else {
                                        continue;
                                    }
                                }
                                setb.add(ne);
                                qb.addLast(ne);
                            }
                        }
                    }
                }
                cntb++;
            }
        }
        System.out.println("NO ANSWER!");
    }
}

简单分析

这题采用BFS,假设每个节点有k个子节点,那么,step次搜索步长,大概将会有k ^ step个搜索空间;采用双向BFS,由于正向搜索一半的高度(步长一半),搜索空间为k ^ (step / 2),所以双向搜索的空间为2 * k * (step / 2)。
假设,k = 10, step = 8
BFS:10 ^ 8 = 1, 0000, 0000
双向BFS:2 * 10 ^ (8 / 2) = 2, 0000
两者相差了将近 5000倍

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值