双向广度优先搜索(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;
}

双向广度优先搜索(Bi-Directional Breadth First Search)算法_第1张图片

你可能感兴趣的:(算法,算法,宽度优先,java)