当前课程知识点:数据结构(上) > 第六章 图 > (c)广度优先搜索 > 06C-7 复杂度
好
回到我们的广度优先搜索算法
它的复杂度是多少呢?
这取决于你的不同实现方法
尤其是图结构自身的实现算法
这里不妨就以我们的实现版本为例
我们将主体的复杂度部分剥离出来
也就是由while以及for所构成的两重循环
我们的第一个问题是
while循环累计会执行多少步呢?
为此我们需要考察这里的出队操作dequeue()
不难验证 在整个算法的过程中
dequeue()只出现这在一处
因此dequeue()执行多少步
整个while循环就会迭代多少步
我们知道在进入while循环之前
队列中只有一个顶点
也就是起始顶点s
然而此后的入队操作却是不定的
在有些迭代步中
可能会连续地执行多步enqueue操作
而在另一些迭代步中
却有可能一次也没有执行
所幸的是我们可以发现每一个顶点
都会入队一次 而且仅一次
因此enqueue操作将累计执行线性次
所以相应地 dequeue()操作也将执行O(n)次
由此可知
整个while循环累计执行恰好O(n)次
然而内部的这层for循环却并非一目了然
为此我们不妨将它拆解为两部分
首先是for循环这条语句本身
其次才是进入这个循环之后所执行的操作
关于for循环本身
需要回顾它的实现机制
我们知道其实就是对顶点v
所对应的那个行向量
进行一次线性的扫描
具体来说 自后向前累计扫过n个单元
因此与外层的while循环组合起来
for循环语句累计需要执行的时间为n×n
所幸的是
我们并不需要对于每一条潜在的边
都实质地进入一次这个内循环
实际上 当前顶点v有多少个邻居
也就会实质地进入几次内循环
因此内层循环的实质操作
累计而言 不过边的总数 也就是e
两项合计 e这一项可以忽略掉
由此我们可以得出一个结论
这样一个算法从渐近意义而言
需要执行n平方的时间
然而我们需要指出的是
这只具有理论上的意义
在实际中却远远不是这样
背后的原因在于
内层循环的for语句本身
所对应的那个渐近O(n)
实际上是非常非常小的
至少在常系数的意义而言是这样
这可以从两个方面加以验证
首先这个for语句对行向量的操作
实际上都非常简单
无非就是逐一地取出其中的每一个元素
并且判断它是否为空
相对于其它更为复杂的基本操作而言
这种基本操作更加的名副其实
而更重要的第二个方面在于
组成每一个行向量的所有元素
不仅在逻辑上是连续的
而且在物理上也是连续的
构成一个紧凑的整体
这样一种物理上的组织和存储方式
可以有效地激活系统的缓冲机制
换而言之 在对整个行向量的访问过程中
所有的元素都有极高的概率处于高速缓存中
在此后的第八章介绍B树时 我们将会指出
任何一级存储 相当于它的高速缓存而言
在访问速度上的差异
会高达5到6个数量级
在实际效果上的这么样一种极大的差异
完全足以抵消理论上的分析结论
因此我们完全可以将这样一个O(n)忽略掉
并代之以常数
因此这样实现的BFS算法
实际的运行性能
更接近于n加上e
当然如果将邻接矩阵改为所谓的邻接表
则可以直接得到这样一个效率
我们可以说这样一个结果
应该已经是不能指望更好的了
原因在于 既然是要遍历
那么至少要对n个顶点中的
所有顶点分别访问一次
也至少需要对每一条边访问一次
当然这样一个结论也是至关重要的
因为正如我们此前所介绍的
无论是这里的BFS
还是稍后要介绍的DFS以及PFS
所有这些遍历算法实际上都是
后面更为具体也更为复杂的
算法的一种基本实现框架
作为所有算法的一个基本框架
它能够达到如此低廉的一个成本
对我们算法设计者而言
这是一个不能再好的消息了
-选课之前
--写在选课之前
--宣传片
-考核方式
--考核方式
-OJ系统说明
--关于OJ
--1-注册与登录
--2-界面与选课
--3-提交测试
-关于课程教材与讲义
--课程教材与讲义
-关于讨论区
--关于讨论区
-微信平台
--html
-PA晋级申请
--PA晋级
-(a)计算
--演示
--(a)计算--作业
-(b)计算模型
-(b)计算模型--作业
-(c)大O记号
-(c)大O记号--作业
-(d)算法分析
-(d)算法分析--作业
-(e)迭代与递归
-(e)迭代与递归--作业
-(xc)动态规划
-- 演示
-(xc)动态规划--作业
-本章测验--作业
-(a)接口与实现
--02A-5 复制
-(a)接口与实现--作业
-(b)可扩充向量
-(b)可扩充向量--作业
-(c)无序向量
--02C-1 概述
--02C-3 插入
--02C-6 查找
--02C-8 遍历
-(c)无序向量--作业
-(d1)有序向量:唯一化
-(d1)有序向量:唯一化--作业
-(d2)有序向量:二分查找
-(d2)有序向量:二分查找--作业
-(d3)有序向量:Fibonacci查找
-(d3)有序向量:Fibonacci查找--作业
-(d4)有序向量:二分查找(改进)
-(d4)有序向量:二分查找(改进)--作业
-(d5)有序向量:插值查找
-第二章 向量(下)--(d5)有序向量:插值查找
-(e)起泡排序
--02E-2 改进
--02E-3 反例
-(e)起泡排序--作业
-(f)归并排序
-(f)归并排序--作业
-本章测验--作业
-(a)接口与实现
--03A-4 实现
-(a)接口与实现--作业
-(b)无序列表
--03B-2 查找
-(b)无序列表--作业
-(c)有序列表
--03C-3 查找
-(c)有序列表--作业
-(d)选择排序
--03D-1 构思
--03D-2 实例
--03D-3 实现
--03D-4 推敲
--03D-6 性能
-(d)选择排序--作业
-(e)插入排序
--03E-1 经验
--03E-2 构思
--03E-3 对比
--03E-4 实例
--03E-5 实现
-(e)插入排序--作业
-(xd)习题辅导:LightHouse
-本章测验--作业
- (a)栈接口与实现
--04A-1 栈
--04A-2 实例
--04A-3 实现
- (a)栈接口与实现--作业
-(c1)栈应用:进制转换
-第四章 栈与队列--(c1)栈应用:进制转换
-(c2)栈应用:括号匹配
-(c2)栈应用:括号匹配--作业
-(c3)栈应用:栈混洗
-第四章 栈与队列--(c3)栈应用:栈混洗
-(c4)栈应用:中缀表达式求值
-(c4)栈应用:中缀表达式求值--作业
-(c5)栈应用:逆波兰表达式
-第四章 栈与队列--(c5)栈应用:逆波兰表达式
-(d)队列接口与实现
--04D-1 接口
--04D-2 实例
--04D-3 实现
-第四章 栈与队列--本章测验
-(a)树
--05A-1 动机
--05A-2 应用
-(a)树--作业
-(b)树的表示
--05B-2 父亲
--05B-3 孩子
-第五章 二叉树--(b)树的表示
-(c)二叉树
-(c)二叉树--作业
-(d)二叉树实现
-(d)二叉树实现--作业
-(e1)先序遍历
-(e1)先序遍历--作业
-(e2)中序遍历
-第五章 二叉树--(e2)中序遍历
-(e4)层次遍历
-第五章 二叉树--(e4)层次遍历
-(e5)重构
-(e5)重构--作业
-本章测验--作业
-(a)概述
-(a)概述--作业
-(b1)邻接矩阵
-(b1)邻接矩阵--作业
-(c)广度优先搜索
--06C-2 策略
--06C-3 实现
--06C-5 实例
-(c)广度优先搜索--作业
-(d)深度优先搜索
--06D-1 算法
--06D-2 框架
--06D-3 细节
-(d)深度优先搜索--作业
-第六章 图--本章测验