简单排序算法之——插入排序
2016-08-09 19:17
267 查看
tips:为了方便理解和学习,我们讲解排序假设是对一个整型数组进行排序(不是整型数组也可以排序,关键是你要排什么的顺序)
这次我们讲解最简单的排序算法之一——插入排序
插入排序的思想跟现实生活中的一些事物是有关系的哦!是什么呢?那就是排队(对应排序)时的插队(对应插入)啦!
让我们假设一个情况:现在排了个队伍,所有人身高不一致,要求整个队伍变为从矮到高的顺序(排序喽~),且你前面所有人都排好了(从你开始往后都没排好),你会怎么做呢?
我赌五毛,你肯定是先和你前面那个人比身高,如果比他矮则“插入”到他前面去,然后继续和前面那个人比身高,直到你前面的人比你矮或者你到最前面(有点尴尬,我最矮吗orz?)了为止。
这就是插入排序的思想来源~(你也可以叫它插队排序
)
但是,实现插入排序还有一个问题要解决,那就是“我前面的所有人都排好了”!
看起来是个大问题呢……
谁敢保证我前面的队伍一定是排好了的啊!
还真有!谁呢?队伍中的第二个人啊!
(我前面就一个人,我前面当然排好了orz)
所以啊,我们只要先让第二个人排(也就是看看他要不要插到第一个人的前面去
)
再让第三个人排,然后第四个人,接着第五个人,以此类推……
讲到这了,我想,插入排序的例程也应该给出啦
void InsertionSort ( ElementType A [ ], int N) //N为数组元素个数
{
int j, p; //j将用于插入时当前元素下标,p用于当前要插队到前面去的家伙
ElementType Temp;
for (p=1; p<N ; p++ )
{
Temp=A [ p ]; //每次循环开始时用Temp保存当前需排序元素,可以免去交换
for ( j=p; j>0 && A [ j-1 ] > Temp ; j--)
A [ j ]=A [ j-1 ]; //因为Temp的存在,我们不需要做真正的交换动作,只需移动
A [ j ]=Temp; //显然,当循环跳出时j对应下标即A [ p ]应该在的位置
}
}
然后再来一个小小的实际例子,看看插入排序是如何工作的吧!
例
5,8,6,9,4,10
第一次 5,8,6,9,4,10 (8和5比较,未交换)
第二次 5,6,8,9,4,10 (6和8,5依次比较,最后与8交换)
第三次 5,6,8,9,4,10 (9和8比较,不交换(注意9未与6和5比较,因为没必要了))
第四次 4,5,6,8,9,10 (4和9,8,6,5依次比较)
第五次 4,5,6,8,9,10 (10和9比较,不交换,结束)
插入排序的时间复杂度是O(N^2),这个界可以在元素是完全反序(例如54321)的情况下达到。
(这么一说,感觉插入排序也不是特别6呢orz……但是啊!它可是希尔排序的思想来源哦!
)
插入排序是O(N^2),冒泡排序也是O(N^2),那它们是不是有什么共通点啊?!
让我们来说一个定理解释一下吧~
通过交换相邻元素进行排序的任何算法平均需要Ω(N^2)(而插入排序和冒泡排序每次都是交换的相邻元素呢)
这个定理我们待会儿再推(因为要用到另外的定理啊(⊙o⊙))
现在我们先讲另一个定理
N个互异数的数组的平均逆序数是N(N-1)/4(这里需要你自己明白什么是逆序数呢)
推:对于任意表,例如5,8,9,6,4,其逆序数为6,而它的反序表为4,6,9,8,5,其逆序数为4。两个表的逆序数之和为10,正是元素总数5选2的组合!即N(N-1)/2!
因为任意一对数(x,y)且x在前又x>y的情况一定会在二表之一中。所以说一个互异数表与其反序表的逆序数之和一定是N(N-1)/2,也就是说任意一个互异数表的平均逆序数为N(N-1)/4
有了这个定理,我们再回去推导第一个定理:
因为逆序数平均为N(N-1)/4=Ω(N^2),而每次交换只会减少一个逆序数!所以需要Ω(N^2)次交换!
由此我们也可以明白一件事:如果要使一个排序算法以亚二次或o(N^2)时间运行,必须执行一些比较,特别要对相距较远的元素进行交换,它必须每次交换删除不止一个逆序数!(如果你看过希尔排序和快速排序,应该很容易看出它们比较过较远的元素吧?~)
这次我们讲解最简单的排序算法之一——插入排序
插入排序的思想跟现实生活中的一些事物是有关系的哦!是什么呢?那就是排队(对应排序)时的插队(对应插入)啦!
让我们假设一个情况:现在排了个队伍,所有人身高不一致,要求整个队伍变为从矮到高的顺序(排序喽~),且你前面所有人都排好了(从你开始往后都没排好),你会怎么做呢?
我赌五毛,你肯定是先和你前面那个人比身高,如果比他矮则“插入”到他前面去,然后继续和前面那个人比身高,直到你前面的人比你矮或者你到最前面(有点尴尬,我最矮吗orz?)了为止。
这就是插入排序的思想来源~(你也可以叫它插队排序
)
但是,实现插入排序还有一个问题要解决,那就是“我前面的所有人都排好了”!
看起来是个大问题呢……
谁敢保证我前面的队伍一定是排好了的啊!
还真有!谁呢?队伍中的第二个人啊!
(我前面就一个人,我前面当然排好了orz)
所以啊,我们只要先让第二个人排(也就是看看他要不要插到第一个人的前面去
)
再让第三个人排,然后第四个人,接着第五个人,以此类推……
讲到这了,我想,插入排序的例程也应该给出啦
void InsertionSort ( ElementType A [ ], int N) //N为数组元素个数
{
int j, p; //j将用于插入时当前元素下标,p用于当前要插队到前面去的家伙
ElementType Temp;
for (p=1; p<N ; p++ )
{
Temp=A [ p ]; //每次循环开始时用Temp保存当前需排序元素,可以免去交换
for ( j=p; j>0 && A [ j-1 ] > Temp ; j--)
A [ j ]=A [ j-1 ]; //因为Temp的存在,我们不需要做真正的交换动作,只需移动
A [ j ]=Temp; //显然,当循环跳出时j对应下标即A [ p ]应该在的位置
}
}
然后再来一个小小的实际例子,看看插入排序是如何工作的吧!
例
5,8,6,9,4,10
第一次 5,8,6,9,4,10 (8和5比较,未交换)
第二次 5,6,8,9,4,10 (6和8,5依次比较,最后与8交换)
第三次 5,6,8,9,4,10 (9和8比较,不交换(注意9未与6和5比较,因为没必要了))
第四次 4,5,6,8,9,10 (4和9,8,6,5依次比较)
第五次 4,5,6,8,9,10 (10和9比较,不交换,结束)
插入排序的时间复杂度是O(N^2),这个界可以在元素是完全反序(例如54321)的情况下达到。
(这么一说,感觉插入排序也不是特别6呢orz……但是啊!它可是希尔排序的思想来源哦!
)
插入排序是O(N^2),冒泡排序也是O(N^2),那它们是不是有什么共通点啊?!
让我们来说一个定理解释一下吧~
通过交换相邻元素进行排序的任何算法平均需要Ω(N^2)(而插入排序和冒泡排序每次都是交换的相邻元素呢)
这个定理我们待会儿再推(因为要用到另外的定理啊(⊙o⊙))
现在我们先讲另一个定理
N个互异数的数组的平均逆序数是N(N-1)/4(这里需要你自己明白什么是逆序数呢)
推:对于任意表,例如5,8,9,6,4,其逆序数为6,而它的反序表为4,6,9,8,5,其逆序数为4。两个表的逆序数之和为10,正是元素总数5选2的组合!即N(N-1)/2!
因为任意一对数(x,y)且x在前又x>y的情况一定会在二表之一中。所以说一个互异数表与其反序表的逆序数之和一定是N(N-1)/2,也就是说任意一个互异数表的平均逆序数为N(N-1)/4
有了这个定理,我们再回去推导第一个定理:
因为逆序数平均为N(N-1)/4=Ω(N^2),而每次交换只会减少一个逆序数!所以需要Ω(N^2)次交换!
由此我们也可以明白一件事:如果要使一个排序算法以亚二次或o(N^2)时间运行,必须执行一些比较,特别要对相距较远的元素进行交换,它必须每次交换删除不止一个逆序数!(如果你看过希尔排序和快速排序,应该很容易看出它们比较过较远的元素吧?~)
相关文章推荐
- 数据结构与算法学习-简单排序算法之插入排序
- java数据结构与算法-简单排序-插入排序
- 基本算法简单实现-二分法查找、合并排序、冒泡排序、插入排序、选择排序、快速排序
- C++简单排序算法之插入排序
- 简单排序算法之插入排序、选择排序和冒泡排序
- 简单排序算法:直接插入排序(插入排序)
- 转载:yangsen600的C语言实现的简单排序算法汇总
- C#中定义数组和一个简单排序算法实现。
- 冒泡排序、选择排序、插入排序 算法实现(C++)
- 算法学习笔记之排序--基于指针的插入排序
- 有意思的排序算法-插入排序
- 算法熟记-排序系列-插入排序
- 采用回调函数的内部排序算法-插入排序,希尔排序,冒泡,快排,堆排,归并排,基数排序
- 简单选择排序的算法实现
- 查找""排序""简单数学计算" "简单算法"[Java实现](数据结构和算法)(复习)(持续更新
- java基本算法总结(冒泡排序、选择排序、插入排序)
- 排序算法之一——插入排序
- 简单排序算法--冒泡排序
- (六)简单排序—插入排序
- 算法学习笔记之排序--基于值的插入排序