广度优先搜索的应用

Posted C语言进阶学习

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了广度优先搜索的应用相关的知识,希望对你有一定的参考价值。

学习难,

难在于当你处于学习过程时对自己的克制,

 编程亦是如此。


前言

C语言进阶学习

前面的文章中我们学习了,分别用这两种算法解决了迷宫问题,但是迷宫问题在实际应用中很少用到,今天我们再次使用广度优先搜索算法来解决一个实际问题——文件搜索。文件搜索为什么要用广度优先搜索而不用深度优先搜索呢?考虑这样一个场景,假设我们在D盘中进行搜索,目标文件在最后一个文件夹里面,而第一个搜索的文件夹又有几万个甚至上十万个文件,这将是一个很漫长的过程。而广度优先搜索则是每个位置都搜一下,平均搜索时间比较平稳。


思路

C语言进阶学习

1. 搜索文件首先得有一个起始路径,而该路径应该由用户提供;
2. 既然是搜索,那么得有关键字才行,而关键字也应该由用户提供;
3. 获取起始路径和关键字后,将起始路径入队,然后开始BFS;
4. 最后将符合搜索条件的文件路径输出到屏幕上,完成。
下面使用流程图来展示算法的搜索过程。


从流程图中可以看出,结束搜索的条件是队列为空,这和迷宫问题的结束条件一致。


数据结构

C语言进阶学习

在迷宫问题中,不管是栈还是队列的实现,都是使用数组来充当。而在本文中,作者重新实现了队列这个数据结构,在需求较为简单时稍微修改即可使用。定义如下:

/** * 队列定义 */struct _queue {
size_t size; /** 队列的元素个数 */ in_queue_callback iqcb; /** 入队时的具体操作回调 */ free_qdata_callback fdcb; /** 释放队列时释放数据部分回调 */ void (*push)(queue* que, const qdata value); /** 入队 */ qdata (*pop)(queue* que); /** 出队 */ _bool (*is_empty)(queue* que); /** 判空 */ q_value* front; /** 队头指针 */ q_value* rear; /** 队尾指针 */};

其中“in_queue_callback”和“free_qdata_callback”的定义如下:

/** * 队列的数据(数据结构定义),这么做是为了方便 * 更换数据结构。 */typedef char * qdata;/** * 数据入队回调,为了使程序具有更高的可移植性,采用 * 回调的方式让调用者自己实现。 */typedef _bool (*in_queue_callback)(qdata* dst, qdata const data);
/** * 释放队列回调,调用者实现。 */typedef void (*free_qdata_callback)(qdata* addr);

在使用时只需要修改qdata的基本类型(可以是复合类型)和实现入队时的赋值操作和释放内存操作即可。因为大部分情况下队列的数据是复合类型(例如迷宫问题),虽然在本文中是字符串类型。当然,如果调用者的数据是基本类型而已,入队时直接赋值即可,释放内存时什么都不用做直接返回就行。

即使对队列进行了封装,但使用起来还得调用者自己编写一些函数,毕竟C语言是面向过程,不像大多数面向对象编程语言可以使用泛型。不过学习算法和数据结构的重点不是编程语言,也就无所谓了。


扩展

C语言进阶学习

搞定了算法和数据结构,接下来就是编写代码了。不过在编写代码之前,还有几个问题:C语言如何遍历文件(夹)?经过查询资料,发现在Windows系统中,官方提供了一些接口可用于遍历文件(夹)。
主要使用到其提供的一种数据结构(变量)和两个算法(函数)。它们的定义如下:

struct _finddata{ unsigned attrib; __time64_t time_create; // -1 for FAT file systems __time64_t time_access; // -1 for FAT file systems __time64_t time_write; _fsize_t size; char name[260];};
int _findfirst(char const* _FileName, struct _finddata* _FindData);
int _findnext(int _FindHandle, struct _finddata* _FindData);

先看数据结构的定义,根据本文的需求,和时间有关成员我们不需要关心,我们只关心两个成员——“attrib”和“name”。“attrib”描述的是文件的属性,“name”是文件名,我们可以通过属性来判断该文件是一个目录还是文件。文件名则是判断是否符合搜索要求的依据。当然,如果想要做得细致些,可以加上日期、文件大小等来进一步提高搜索的准确度。

调用“_findfirst”函数,第一个参数为“路径”,第二个参数用于存放在该路径下找到的第一个文件(夹),如果该路径下存在文件,则返回一个“句柄”,在调用“_findnext”查找下一个文件时作为参数传递。

由于代码较多,就不在这里张贴了,有兴趣的读者可以自行git,当然自己动手练习更好。
本来只是想简单介绍一下广度优先搜索算法的应用,想着想着就想把它开发成一个小工具(仅限于Windows)使用。现该工具的功能主要有以下几点:
  1. 遍历并打印整个盘符的所有文件,装十三用,没有实际意义;

  2. 搜索文件,目前只能进行模糊搜索,下文所说的搜索均为模糊搜索;

  3. 搜索出所有视频、音频、图片以及某一类文件,包括隐藏文件;

  4. 只搜索所有的隐藏文件。


源码

 
详细用法请参阅工具文档,源代码已放在“码云”上。

链接:https://gitee.com/Aiven-li/c-set.git





如果你有想学或者正在学习C语言的好友,长按二维码图即可分享。







也可以长按二维码图添加作者微信交流。



以上是关于广度优先搜索的应用的主要内容,如果未能解决你的问题,请参考以下文章

七十九深度和广度优先搜索算法

广度优先搜索的实际应用

广度优先搜索在树中的应用

图解:深度优先搜索与广度优先搜索

广度优先搜索的应用

广度优先搜索及其应用