当前课程知识点:数据结构(上) > 第三章 列表 > (e)插入排序 > 03E-8 逆序对
在结束这一节之前
我们再来讨论与插入排序非常相关
也是非常有意思的一个问题
我们来介绍这么样一个概念
也就是逆序对 inversion
对于任何一个序列
我们都可以在其中发现这种逆序对
既然是对 肯定就涉及到两个元素
没错
如果有某两个元素一左一右
而且左侧更大一些 右侧更小一些
我们就称它们构成了一个逆序对 一个inversion
当然反过来 如果是这样的顺序的
也就是左小右大的形式
这就不是个逆序对
当然你也可以称它叫作顺序对
实际上 在整体的n个元素中
任何两个元素都可能构成一个逆序对
因此 对于一个长度为n的序列而言
逆序对的总数有可能会多达n平方
那么inversion和我们这一节的主题
insertion sort是什么关系呢?
为了说明方便
我们先还要做一个铺垫的工作
我们看到inversion实际上
涉及到的是两个元素
这样不利于我们记帐
我们不妨采用一个统一的习惯 也就是
将每一个inversion都记入到后边
也就是靠右的那个元素的帐上去
因此我们只需要
在这种意义下 统计每一个元素
所对应的inversion有多少个
那么累计而言 就得到了
整个序列中所蕴含的逆序对的数目
再强调一遍 我们将每一个逆序对
都记到后者的帐上去
于是对于任何的一个节点p
我们都可以依此来定义
它所对应的逆序对的数目 记作i(p)
所有节点p所对应逆序对数的总和
也就是 整个序列所包含逆序对的总数
好了
回到插入排序的一般的场景
也就是说 如果当前有序的前缀为s
我们就要将无序后缀中的首元素
插入到这个前缀中的适当位置
正像我们此前所讲过的
这个对应的位置是一个切分点
在它之前 都是比待插入元素e
不大的元素
而反过来呢
在这个分界点之后的那些元素
都应该严格大于这个待插入的元素e
左边都不大于e 而右边都大于e
这说明什么呢?
这说明这个区间内的任何一个元素
都与待插入元素e构成了一个inversion
而且这个inversion正好能归入到
刚才我们记帐于p的那样一个统计值中
或者反过来讲
p所对应的inversion有多少个
p就需要经过多少次比较
才能抵达最终的插入位置
从这个意义上说
i(p)其实就是p所对应的查找长度
因此 如果我们将整个序列中
所包含的逆序对数 记作大I
那么这个大写的I
实际上 也就对应于插入排序算法
在一次又一次定位搜索过程中
比较次数的累计总和
而这个我们说过
就是这个算法所消耗时间的最主要部分
当然不要忘了还有另一部分
也就是对元素真正实施
插入操作所花费的时间
每个元素各自是1 累计是n
所以确切地讲
插入排序所需要的时间
应该是这两个量的总和
而刚才我们所分析的
最好情况和最坏情况呢
无非是它的特例
在最好情况下
我们讲过 它的复杂度是n
为什么是n呢?
回忆一下 最好情况
就是所有的元素都是顺序输入的
逐次递增
我们可以看到 在这样的一个序列中
根本就不含任何的逆序对
逆序对的总数是0
那么0加上n 当然也就是n了
反过来 再看最坏的情况
也就是 完全逆序输入的情况
这个时候我们说过
它的复杂度是n平方
为什么是n平方呢?
因为对于这样一种输入序列而言
其中任何一对元素都是逆序的
因此 它的逆序对的总数
当然也就等于
所有n个元素两两组合的总数
这个总数恰好就是n平方量级的
概括一下 从逆序对的角度来看
可以将插入排序算法的复杂度
度量为这样的一个结果
而这个结果向下可以很好的解释
并且对应于最好的情况
向上也可以很好的解释
并对应于最坏的情况
它是一个更一般的表示
也是一个更加本质的解释
就这样的意义而言
我们如果把刚才的逆序对总数
也就是I 作为序列无序程度的度量尺度
那么insertion sort
就可以理解为是
通过一次一次的努力
去修复这种无序性
原来有多无序 它就需要多少次修复
因此它的算法复杂度
其实不光是取决于问题的规模
而更多的是取决于
输入本身所具有的特性
也就是它的无序程度
所以这样一种算法
也称作输入敏感的
input-sensitive
在排序算法家族中
并非每一种算法都具有这样的一个特性
而插入排序 也正因为它具有这样一个特性
而显得非常的独特
在我们最终要介绍的希尔排序中
我们将会看到
这种特性对于希尔排序整体的性能
乃至这个算法的有效性
都是至关重要的
-选课之前
--写在选课之前
--宣传片
-考核方式
--考核方式
-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)深度优先搜索--作业
-第六章 图--本章测验