双向广度优先搜索(Bi-Directional Breadth First Search)算法

双向广度优先搜索(Bi-Directional Breadth First Search)

双向广度优先搜索是对广度优先搜索的优化,但是有一个使用条件:搜索路径可逆。
搜索原理
双向广搜是同时从开始状态和目标状态展开搜索的,这样就会产生两棵搜索状态树。我们想象一下,让起始于开始状态的树从上往下生长,再让起始于目标状态的树从下往上生长,同时在它们的生长空间中遍布着一个一个的状态结点,等待着这两棵树延伸去触及。
由于任一个状态都是唯一存在的,当两棵搜索树都触及到了某个状态时,这两棵树就出现了交叉,搜索即告结束。
让两棵树从发生交叉的状态结点各自原路返回构建路径,然后算法把两条路径拼接起来,即为结果路径。
可用条件
对于拼图游戏来说,已经知道了开始状态(某个乱序的状态)和目标状态(图片复原时的状态),而这两个状态其实是可以互换的,完全可以从目标复原状态开始搜索,反向推进,直到找出拼图开始时的乱序状态。所以,我们的拼图游戏是路径可逆的,适合双向广搜。
单线程下的双向广搜
要实现双向广搜,并不需要真的用两条线程分别从开始状态和目标状态对向展开搜索,在单线程下也完全可以实现,实现的关键是于让两个开放队列交替出列元素。
在每一次循环中,比较两个开放队列的长度,每一次都选择最短的队列进行搜索,优先让较小的树生长出子结点。这样做能够使两个开放队列维持大致相同的长度,同步增长,达到均衡两棵搜索树的效果。

- (NSMutableArray *)search {
    if (!self.startStatus || !self.targetStatus || !self.equalComparator) {
        return nil;
    }
    NSMutableArray *path = [NSMutableArray array];
    
    // 关闭堆,存放已搜索过的状态
    NSMutableDictionary *positiveClose = [NSMutableDictionary dictionary];
    NSMutableDictionary *negativeClose = [NSMutableDictionary dictionary];
    
    // 开放队列,存放由已搜索过的状态所扩展出来的未搜索状态
    NSMutableArray *positiveOpen = [NSMutableArray array];
    NSMutableArray *negativeOpen = [NSMutableArray array];
    
    [positiveOpen addObject:self.startStatus];
    [negativeOpen addObject:self.targetStatus];
    
    while (positiveOpen.count > 0 || negativeOpen.count > 0) {
        // 较短的那个扩展队列
        NSMutableArray *open;
        // 短队列对应的关闭堆
        NSMutableDictionary *close;
        // 另一个关闭堆
        NSMutableDictionary *otherClose;
        // 找出短队列
        if (positiveOpen.count && (positiveOpen.count < negativeOpen.count)) {
            open = positiveOpen;
            close = positiveClose;
            otherClose = negativeClose;
        }
        else {
            open = negativeOpen;
            close = negativeClose;
            otherClose = positiveClose;
        }
        
        // 出列
        id status = [open firstObject];
        [open removeObjectAtIndex:0];
        
        // 排除已经搜索过的状态
        NSString *statusIdentifier = [status statusIdentifier];
        if (close[statusIdentifier]) {
            continue;
        }
        close[statusIdentifier] = status;
        
        // 如果本状态同时存在于另一个已检查堆,则说明正反两棵搜索树出现交叉,搜索结束
        if (otherClose[statusIdentifier]) {
            NSMutableArray *positivePath = [self constructPathWithStatus:positiveClose[statusIdentifier] isLast:YES];
            NSMutableArray *negativePath = [self constructPathWithStatus:negativeClose[statusIdentifier] isLast:NO];
            // 拼接正反两条路径
            [positivePath addObjectsFromArray:negativePath];
            path = positivePath;
            break;
        }
        
        // 否则,扩展出子状态
        [open addObjectsFromArray:[status childStatus]];
    }
    NSLog(@"总搜索数量: %@", @(positiveClose.count + negativeClose.count - 1));
    return path;
}

cafa2f0f0edb1f28ef3487df9e8f8b50.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

神仙别闹

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值