当前课程知识点:数据结构(上) > 第二章 向量(下) > (e)起泡排序 > 02E-4 再改进
重新审视这个例子 我们会发现
所多余出来的时间消耗
无非是在后缀中
对这些已就位元素的反复扫描交换
不难理解
这些元素都是不必扫描交换的
可惜我们此前的那个算法版本
未能及时地将它们分解出来
它们实际上是可以分解出来的
比如说 如果我们通过某一种方法
记录在上一趟扫描交换过程中
所进行的最后一次交换
我们就很容易确定
在上一趟扫描的区间中
有一个多长的后缀
实际上没有做过任何交换
也就是说 它们中的元素
都是已经就位了的
如果能这样
只需要将原先的右侧标志hi
直接地指向这个新的位置
而不是像刚才那样
亦步亦趋地、逐个地收缩
基于以上的分析
不难得到一个新的改进的方法
从结构上看 跟刚才大体类似
依然是逐个地检查所有的相邻对
如果是逆序的 就做交换
如果说有不同的话
就在于这里我们所记录的
不再只是一个逻辑性变量
而是一个名为last的整型 或者说是秩
这么样一个变量
它的初值是取作lo
而每当需要交换
就将这个last更新为新的位置
我们注意到 在整个算法的过程中
lo这个变量是持续递增的
从来不会减少
所以换而言之
当它在返回的时候
last确实名副其实地记录了
最右侧 也就是最后一对逆序对的位置
这样的话 我们就可以
有效地来处理刚才那种情况
回到刚才那个实例
即便我们构造了一个
足够短的乱序前缀
再加一个非常长
但是已经就绪了的后缀
我们新的这个算法
首先也会做一趟扫描交换
当然 为此花费的时间是O(n)
但是与刚才那个版本的不同
在这个时候
它会聪明地检测出
发生的最后一次扫描交换
绝对不会超过这个位置
我们完全可以将
扫描交换的右侧界桩hi
一次性地挪到这儿
这等效于聪明地判断出了
此后的这些元素
包括最后那个元素
都是已经就位的
从算法的流程来说
我们的下一趟扫描交换的区间
就不再是原先整个那个绿色的区间
而是相对要短很多的
这样的一个区间
接下来的故事呢?
等效于只是对这样
一段区间做扫描交换
因此我们需要花费的时间
除了刚才的O(n)以外
主要是对应于这样的一个更小的三角形
如果按照刚才所设计的那个例子
边长是根号n的话
累计也不过是再加上一个O(n)
与刚才这个O(n)合并
总体不过是O(n)
更有意思的是
这种情况在整个排序过程中
有可能会多次出现
即便最初的输入向量是无序的
我们也确实需要进行
若干次的扫描交换
但是当执行到某一次迭代之后
我们有可能会发现
当前区间的某一个后缀
已经完全有序了
从而按照刚才的算法
我们会聪明地对
这样的一个新的区间
来做进一步的扫描交换
接下来也可能确实又会
经过若干次扫描交换
同样地 又有可能在某一个后缀中
发现所有的元素都已就位
于是我们又会继续跳过这些元素
从更紧凑的范围 继续执行这个算法
可见 通过这样的一个策略
这个算法确实可以省去
这部分 这部分
以及相应一些部分的时间
我们也可以通过图形的方式
形象地将新的这个算法版本
与之前的原始版本
在时间效率上做一个对比
这个三角形 我们应该很熟悉了
它代表的就是原始的起泡排序算法
所需要的时间
在新的这样的一个版本中
这个算法所需要执行的扫描交换
将会呈现为连续的一段
然后再间或地跳跃到下面一段
以及再间或地有可能会跳跃到下面一段
换而言之 这个算法的时间成本
将取决于这样一个一个
若干个梯形的面积总和
相对于此前那个梯形来说
这种梯形的划分更加的精细
所以它节省下来的时间
也会在通常的情况下 相对更多
当然在最坏的情况下
这个算法依然是于事无补的
起泡排序依然注定需要O(n^2)的时间
-选课之前
--写在选课之前
--宣传片
-考核方式
--考核方式
-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)深度优先搜索--作业
-第六章 图--本章测验