算法:队列与广度优先搜索(迷宫问题)

队列也是一组元素的集合,也提供两种基本操作:Enqueue(入队)将元素添加到队尾,Dequeue(出队)从队头取出元素并返回。就像排队买票一样,先来先服务,先入队的人也是先出队的,这种方式称为FIFO(First In First Out,先进先出),有时候队列本身也被称为FIFO。

下面我们用队列解决迷宫问题。程序如下:(参考《linux c 编程一站式学习》)

/*************************************************************************
> File Name: breadth_search.c
> Author: Simba
> Mail: dameng34@163.com
> Created Time: 2012年12月24日 星期一 19时24分37秒
************************************************************************/

#include<stdio.h>


#define MAX_ROW 5
#define MAX_COL 5

struct point
{
    int row, col, predecessor;
} queue[512];

int head = 0, tail = 0;

void enqueue(struct point p)
{
    queue[tail++] = p;
}

struct point dequeue(void)
{
    return queue[head++];
}

int is_empty(void)
{
    return head == tail;
}

int maze[MAX_ROW][MAX_COL] =
{
    {0, 1, 0, 0, 0},
    {0, 1, 0, 1, 0},
    {0, 0, 0, 0, 0},
    {0, 1, 1, 1, 0},
    {0, 0, 0, 1, 0},
};

void print_maze(void)
{
    int i, j;
    for (i = 0; i < MAX_ROW; i++)
    {
        for (j = 0; j < MAX_COL; j++)
            printf("%d ", maze[i][j]);
        putchar('\n');
    }
    printf("*********\n");
}

void visit(int row, int col)
{
    struct point visit_point = { row, col, head - 1 };
    maze[row][col] = 2;
    enqueue(visit_point);
}

int main(void)
{
    struct point p = { 0, 0, -1 };

    maze[p.row][p.col] = 2;
    enqueue(p);

    while (!is_empty())
    {
        p = dequeue();
        if (p.row == MAX_ROW - 1    /* goal */
                && p.col == MAX_COL - 1)
            break;
        if (p.col + 1 < MAX_COL /* right */
                && maze[p.row][p.col + 1] == 0)
            visit(p.row, p.col + 1);
        if (p.row + 1 < MAX_ROW /* down */
                && maze[p.row + 1][p.col] == 0)
            visit(p.row + 1, p.col);
        if (p.col - 1 >= 0  /* left */
                && maze[p.row][p.col - 1] == 0)
            visit(p.row, p.col - 1);
        if (p.row - 1 >= 0  /* up */
                && maze[p.row - 1][p.col] == 0)
            visit(p.row - 1, p.col);
        print_maze();
    }
    if (p.row == MAX_ROW - 1 && p.col == MAX_COL - 1)
    {
        printf("(%d, %d)\n", p.row, p.col);
        while (p.predecessor != -1)
        {
            p = queue[p.predecessor];
            printf("(%d, %d)\n", p.row, p.col);
        }
    }
    else
        printf("No path!\n");

    return 0;
}

输出为:

其实仍然可以像《用深度优先搜索解迷宫问题》一样用predecessor数组表示每个点的前趋,但我们可以换一种更方便的数据结构,直接在每个点的结构体中加一个成员表示前趋: struct point { int row, col, predecessor; } queue[512];

int head = 0, tail = 0; 变量head和tail是队头和队尾指针,head总是指向队头,tail总是指向队尾的下一个元素。每个点的predecessor成员也是一个指针,指向它的前趋在queue数组中的位置。如下图所示:

这是一个静态分配的数组,每个数组元素都有row、col和predecessor三个成员,predecessor成员保存一个数组下标,指向数组中的另一个元素,这其实也是链表的一种形式,称为《静态链表》。

为了帮助理解,把这个算法改写成伪代码如下图:

从打印的搜索过程可以看出,这个算法的特点是沿各个方向同时展开搜索,每个可以走通的方向轮流往前走一步,这称为广度优先搜索(BFS,Breadth First Search)。探索迷宫和队列变化的过程如下图所示。

广度优先是一种步步为营的策略,每次都从各个方向探索一步,将前线推进一步,图中的虚线就表示这个前线,队列中的元素总是由前线的点组成的,可见正是队列先进先出的性质使这个算法具有了广度优先的特点。广度优先搜索还有一个特点是可以找到从起点到终点的最短路径,而深度优先搜索找到的不一定是最短路径。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏算法channel

常用排序算法代码兑现

主要推送关于对算法的思考以及应用的消息。坚信学会如何思考一个算法比单纯地掌握100个知识点重要100倍。本着严谨和准确的态度,目标是撰写实用和启发性的文章,欢迎...

34011
来自专栏一个会写诗的程序员的博客

《Kotin 极简教程》第8章 函数式编程(FP)(1)第8章 函数式编程(FP)《Kotlin极简教程》正式上架:

"函数式编程", 又称泛函编程, 是一种"编程范式"(programming paradigm),也就是如何编写程序的方法论。它的基础是 λ 演算(lambda...

772
来自专栏人工智能LeadAI

算法 | 排序算法图形化比较:快速排序、插入排序、选择排序、冒泡排序

用Objective-C实现几种基本的排序算法,并把排序的过程图形化显示。其实算法还是挺有趣的 。 选择排序 冒泡排序 插入排序 快速排序 01 选择排序 以升...

2927
来自专栏java思维导图

经典面试问题: Top K 之 -- 海量数据找出现次数最多或,不重复的

林冠宏 / 指尖下的幽灵 仅列举一些解决方法,事实的解决方案是非常多的。 这些问题都是面临着有如下的考虑: 内存不足以放下所有的数。 机器CPU的核数不够。 ...

3597
来自专栏Java3y

Map集合、散列表、红黑树介绍

1353
来自专栏好好学java的技术栈

“365算法每日学计划”:java语言基础题目及解答(11-15打卡)

自从开始做公众号开始,就一直在思考,怎么把算法的训练做好,因为思海同学在算法这方面的掌握确实还不够。因此,我现在想做一个“365算法每日学计划”。

641
来自专栏数据结构与算法

Splay详解(二)

前言 在上一节中,我们讲述了Splay的核心操作rotate与splay 本节我会教大家如何用这两个函数实现各种强大的功能 为了方便讲解,我们拿这道题做例题...

3136
来自专栏杨建荣的学习笔记

关于ORA-01779问题的分析和解决 (r4笔记第22天)

最近同事问我一个问题,是关于一个update语句的问题,需求有点特别,结果在使用update语句尝试了各种方法后,仍然是不依不饶的报出ORA-01779的错误。...

2857
来自专栏冰霜之地

Google S2 中的四叉树求 LCA 最近公共祖先

首先需要回顾一下希尔伯特曲线的生成方式,具体代码见笔者上篇文章的分析,在这个分析中,有4个方向比较重要,接下来的分析需要,所以把这4个方向的图搬过来。

633
来自专栏猿人谷

为什么使用抽象类?有什么好处?

最简单的说法也是最重要的理由:接口和实现分离 老是在想为什么要引用抽象类,一般类不就够用了吗。一般类里定义的方法,子类也可以覆盖,没必要定义成抽象的啊。 看了下...

1699

扫码关注云+社区