广度搜索图、树优化以及通用模板

一、问题

图、树的遍历算法当中有广度优先搜索与深度优先搜索,本文讲述广度优先搜索的优化。如图所示,有一个需求,从start开始找出到end的所有路径,如果不加限制暴力广度优先搜索的话,每一个节点都需要去访问看是否可以到目标终点,找出目标路径需要遍历2^5 次,但是当图或者树的深度过深时,遍历节点的数量会以以2为底的指数形式增长。比如树的深度为20时需要搜索2^20=1048576次,这时候就算仍然能找出路径,但是对于计算机的资源消耗可能会非常大,计算时间也会非常慢,对用户是非常不友好的。
在这里插入图片描述

二、情景故事

想象一下:你第一次去你老丈人家里做客,你到了小区门口只知道你老丈人家住在2401室,你也不知道在哪栋楼里面,这个时候手机还没电了,所以你先到各个楼下看看女朋友有没有下来接你,结果没有,然后又坐电梯上去一个一个去找,上去发现不是老丈人家的2401又下来,吭哧吭哧把小区所有的2401室都找完了,终于找到了去老丈人家的路。

场景2:你还是第一次去老丈人家做客,你还是只知道老丈人家住在2401室,手机没电了,你还是一个楼一个楼坐电梯找。但是你有一个关心你的女朋友发现你快到了,她肯定知道去楼下的路,就出门,坐电梯,下楼去找你,到了楼下,你刚好找了一个楼下楼碰到了,这时候你肯定知道她住在哪栋楼,怎么坐电梯上楼,你就跟着她上楼去了,其他的楼都不用再找了。(双向奔赴的爱情真美好)

三、优化思路

观察上图结构,发现有很多节点根本是到不了目标节点的,如果能把这些多余的消耗避免掉,复杂度能够极大的下降。
在这里插入图片描述
根据二中的场景故事,则是发现如果终点也能出来找找我们,我们会少走很多不必要的路的。
回到算法当中就是维护两个队列,分别从起点向后找、终点向前找,每次从队列节点数小的一方开始搜索,当两个队列中的访问节点相遇时,一个节点肯定知道了对方来时的路,合在一起便是可通的路。

新的搜索过程如下图所示:
在这里插入图片描述
这样做优化了非常多的节点,第一条产生出来的路径必然是最优路径。实际应用中也可以根据此方法进行扩展,我对我们系统中的路径搜索进行了优化之后速度还是非常快的。如果找的不止一条,有一些不合理路径可以根据最优深度进行过滤,比如单向深度大于最优深度的三分之二等等

四、通用模板

用java写的:

/**
     * 搜索点对点主路经
     *
     * @param startCode      起点设备
     * @param targetCodes    终点设备
     * @param allPath        找出来的所有路径
     */
    public static void findMainPath(String startCode, String targetCode, Map<String, List<String>> allPath) {
        // 节点
        MapNode startNode = SearchMapServiceImpl.searchMap.get(startCode);
        MapNode targetNode = SearchMapServiceImpl.searchMap.get(targetCode);
        // 双向搜索队列
        Queue<String> startNodeQueue = new LinkedList<>();
        Queue<String> targetNodeQueue = new LinkedList<>();

        String startDeviceType = startNode.deviceAttr.get(Constants.SERVICE_TYPE).toString();
        String targetDeviceType = startNode.deviceAttr.get(Constants.SERVICE_TYPE).toString();
        // 把起点加进来
        startNodeQueue.add(startCode + Constants.ROUTE_SPLIT + startDeviceType);
        // 把目标加进来
        targetNodeQueue.add(targetCode + Constants.ROUTE_SPLIT + targetDeviceType);

        // 起点访问标志位
        Set<String> startFindFlagMap = new HashSet<>();
        // 终点访问标志位
        Set<String> targetFindFlagMap = new HashSet<>();
        startFindFlagMap.add(startCode);
        targetFindFlagMap .add(targetCode);
        while (!startNodeQueue.isEmpty()&& !targetNodeQueue.isEmpty()) {
            // 每次从队列个数最小的一端搜索
            if(startNodeQueue.size() <= targetNodeQueue.size()){
                // 走的路
                for (int i = 0; i < startNodeQueue.size(); i++) {
                    String footRoute = startNodeQueue.poll();
                    // 找出footRoute 下一个节点入队列,直到终点队列访问过下一个基点
                }
            }else{
                // 走的路
                for (int i = 0; i < targetNodeQueue.size(); i++) {
                    String footRoute = targetNodeQueue.poll();
                    // 找出footRoute 下一个节点入队列,直到起点队列访问过下一个基点
                }
            }
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值