程序算法艺术与实践经典排序算法之Insertion Sort

插入排序(Insertion Sort)的基本思想是每次将一个待排序的记录,按其关键字大小插入到前面已经排好序的子文件中的适当位置,直到全部记录插入完成为止。

基本思想与伪代码

经过j-1遍处理后,A[1..j-1]己排好序。第j遍处理仅将A[j]插入L[1..j-1]的适当位置,使得A[1..j]又是排好序的序列。要达到这个目的,我们可以用顺序比较的方法。首先比较A[j]和A[j-1],如果A[j-1]≤ A[j],则A[1..j]已排好序,第i遍处理就结束了;否则交换A[j]与A[j-1]的位置,继续比较A[j-1]和A[j-2],直到找到某一个位置i(1≤i≤j-1),使得A[j] ≤A[j+1]时为止。用伪码描述如下:

算法: InsertSort(A,n)
输入:n个数组A
输出:按照递增顺序的数组A

1. for j <- 2 to n do
2.    x<-A[j]
3.    i <- j-1                       //行3到行7把A[j]插入A[1.....j-1]之中
4.    while i >0 and x <A[i] do
5.           A[i+1] <- A[i]
6.           i<- i-1
7.           A[i+1] <- x

如下图所示演示了对4个元素进行插入排序的过程,共需要(a),(b),(c)三次插入。对4个元素进行插入排序

在插入排序算法中,为了写程序方便我们可以引入一个哨兵元素A[0],它小于A[1..n]中任一记录。所以,我们设元素的类型ElementType中有一个常量-∞,它比可能出现的任何记录都小。如果常量-∞不好事先确定,就必须在决定A[i]是否向前移动之前检查当前位置是否为1,若当前位置已经为1时就应结束第i遍的处理。另一个办法是在第i遍处理开始时,就将A[i]放入A[0]中,这样也可以保证在适当的时候结束第i遍处理。

效率分析

考虑算法Insertion_Sort的复杂性。对于确定的i,内while循环的次数为O(i),所以整个循环体内执行了∑O(i)=O(∑i),其中i从2到n。即比较次数为O(n2)。如果输入序列是从大到小排列的,那么内while循环次数为i-1次,所以整个循环体执行了∑(i-1)=n(n-1)/2次。由此可知,最坏情况下,Insertion_Sort要比较Ω(n^2)次。如果元素类型是一个很大的纪录,则算法第5行要消耗大量的时间,因此有必要分析移动元素的次数。经过分析可知,平均情况下第5行要执行n(n-1)/4次,分析方法与冒泡排序的分析相同。如果移动元素要消耗大量的时间,则可以用链表来实现线性表。

Insertion_Sort实现

根据插入排序伪码思想,实现算法代码如下所示:

 void InsertSort(int *a,int n) {
        int i,j,t,m;
        for (i=1;i<n;i++)  {
            t=a[i];
            j=i-1;
            while(j>=0 && t<a[j])  {
                a[j+1]=a[j];
                j--;
            }
            a[j+1]=t;
        }
    } 

我们对上述实现codes,感觉并不是喜欢编程风格(^_^)。那么如下代码呢>_<:

void InsertionSort(int *ptritems, int count) {
    int i, j,  k;                                        

    for (i=1; i< count;  ++i) {
        k = ptritems[i];
		for ( j=i-1; (j>=0) && (k<ptritems[j]); j--)
			ptritems[j+1] = ptritems[j];                 

		ptritems[j+1] = k;
    }
}

测试之前,在此给出部分Main函数的部分code.其中约定N为20(输入数组大小),同时,为了让输入数组的值是随机产生,使用rand()函数。代码如下所示:

int array[N],i;           //声明数组
srand(time(NULL));  

for(i=0;i<N;i++){        //初始化数组
	array[i]=rand()/1000+10;
}  

printf("Sort Before:\n");
for(i=0;i<N;i++) {
	printf("%d ",array[i]);   //输出
}
printf("\n");  

InsertSort(array,N);//排序  

printf("Sort After:\n");
for(i=0;i<N;i++)  {
	printf("%d ",array[i]);   //输出
}  

在这段程序中,主函数首先初始化一个数组,并输出排序前的数组内容。然后,调用插入排序法子函数,接着输出排序后的数组内容。

在插入排序法子程序中,首先将需要插入的元素保存到变量t中。变量j表示插入的位置(插入数组元素的序号),设置其值为i-1,表示准备将当前位置(序号为i)的数插入到序号为i-1(即前一个元素)的位置。

接着,通过while循环判断,如果序号为j元素的数据大于变量t(需要插入的数据),则将序号为j的元素向后移,同时j-1,以判断前一个数据是否还需要向后移。通过该循环,找到一个元素的值比t小,该元素的序号为j。然后,将在序号为j的下一个元素插入数据。

编译执行这段程序,得到如下结果,如下图所示。

插入排序法在数据已有一定顺序的情况下,效率较好。但如果数据无规则,则需要移动大量的数据,其效率就与冒泡排序法和选择排序法一样差了。插入排序法是一个原地置换排序法,也是一个稳定排序法。插入法虽然在最坏情况下复杂性为θ(n^2),但是对于小规模输入来说,插入排序法是一个快速的原地置换排序法。许多复杂的排序法,在规模较小的情况下,都使用插入排序法来进行排序,比如快速排序和桶排序。

关于程序算法艺术与实践更多讨论与交流,敬请关注本博客和新浪微博songzi_tea.

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-19 08:20:06

程序算法艺术与实践经典排序算法之Insertion Sort的相关文章

程序算法艺术与实践:递归策略之递归,循环与迭代

众所周知,递归的实现是通过调用函数本身,函数调用的时候,每次调用时要做地址保存,参数传递等,这是通过一个递归工作栈实现的,同时影响效率的.递归是利用系统的堆栈保存函数当中的局部变量来解决问题的,而递归就是在栈处理栈上一堆的指针指向内存中的对象,这些对象一直不被释放,直到递归执行到最后一次后,才释放空间. 循环效率与递归效率 递归与循环是两种不同的解决问题的典型思路.当然也并不是说循环效率就一定比递归高,递归和循环是两码事,递归带有栈操作,循环则不一定,两个概念不是一个层次,不同场景做不同的尝试.

程序算法艺术与实践之二:函数的渐近的界

众所周知,算法所需的时间应当是随着其输入规模增长的,而输入规模与特定具体问题有关.对大多数问题来说其最自然的度量就是输入中的元素个数.算法的运行时间是指在特定输入时所执行的基本操作数.我们可以得到关于一个关于输入规模n的所需时间的函数.然而可以进一步简化算法的时间分析,我们进行进一步抽象,首先,忽略每条语句的真实代价,通过运行时间的增长率来度量一个算法在时间方面的表现.我们只考虑公式的最高次项,并忽略它的常数系数.本博文主要介绍一些相关的数学知识即:函数的渐近的界的定义与性质.常用的证明方法.

七种经典排序算法最全攻略

经典排序算法在面试中占有很大的比重,也是基础.包括冒泡排序,插入排序,选择排序,希尔排序,归并排序,快速排序,堆排序.希望能帮助到有需要的同学.全部程序采用JAVA实现. 本篇博客所有排序实现均默认从小到大. 一.冒泡排序 BubbleSort 介绍: 冒泡排序的原理非常简单,它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来. 步骤: 比较相邻的元素.如果第一个比第二个大,就交换他们两个. 对第0个到第n-1个数据做同样的工作.这时,最大的数就"浮"到了

十大经典排序算法总结

前言 读者自行尝试可以想看源码戳这,博主在github建了个库,读者可以Clone下来本地尝试.此博文配合源码体验更棒哦~~~ 个人博客:Damonare的个人博客 原文地址:十大经典算法总结 这世界上总存在着那么一些看似相似但有完全不同的东西,比如雷锋和雷峰塔,小平和小平头,玛丽和马里奥,Java和javascript-.当年javascript为了抱Java大腿恬不知耻的让自己变成了Java的干儿子,哦,不是应该是跪舔,毕竟都跟了Java的姓了.可如今,javascript来了个咸鱼翻身,几

经典排序算法(Java实现)

以下程序均将数据封装于DataWrap数据包装类中,如下所示: 1 //数据包装类 2 class DataWrap implements Comparable<DataWrap> 3 { 4 int data; 5 String flag; 6 public DataWrap(int data,String flag) 7 { 8 this.data = data; 9 this.flag = flag; 10 } 11 //重写compareTo方法 12 public int compa

经典排序算法(Java版)

经典排序算法(Java版)  转载 1.冒泡排序 Bubble Sort最简单的排序方法是冒泡排序方法.这种方法的基本思想是,将待排序的元素看作是竖着排列的“气泡”,较小的元素比较轻,从而要往上浮.在冒泡排序算法中我们要对这个“气泡”序列处理若干遍.所谓一遍处理,就是自底向上检查一遍这个序列,并时刻注意两个相邻的元素的顺序是否正确.如果发现两个相邻元素的顺序不对,即“轻”的元素在下面,就交换它们的位置.显然,处理一遍之后,“最轻”的元素就浮到了最高位置:处理二遍之后,“次轻”的元素就浮到了次高位

程序员必须要掌握的排序算法以及查找方法

程序员必须要掌握的排序算法以及查找方法 经典的排序算法要一个一个的弄懂.借个图,列出学习提纲. 三种查找算法:顺序查找,二分法查找(折半查找),分块查找,散列表 2016-04-17 09:08:04

C/C++中的经典排序算法总结

C/C++中的经典排序算法总结 在C/C++中,有一些经典的排序算法,例如:冒泡排序.鸡尾酒排序或双向冒泡排序(改进的冒泡排序).选择排序.直接插入排序.归并排序.快速排序.希尔排序和堆排序等等.下面对这些排序算法进行一一解析并给出示例代码以共享之. 1.冒泡排序 冒泡排序是最基本的排序算法,之所以称之为冒泡排序是因为在冒泡排序的过程中总是大数往前放,小数往后放,相当于气泡上升. 冒泡排序的基本原理是:依次比较相邻的两个数,将大数放在前面,小数放在后面. 影响冒泡排序算法性能的主要部分就是循环和

Jerry 2017年的五一小长假:8种经典排序算法的ABAP实现

2017年4月29日~5月1日,国际劳动节, 三天的小长假. 在国内,小长假往往是这样的: 然而我当时在戏称为"德村"(德国农村)的Walldorf出差并且住在Wiesloch, 这里的五一小长假能听见鸟叫,虫鸣,和风吹过的声音,除此之外再无其他. 街道上别说行人了,连行驶的汽车都很少. 如果一个在成都习惯了热闹生活的人,到了这种乡下地方来估计会觉得百无聊赖.当时国内有同事建议我小长假去德国其他地方转转,然而作为一个30年资深宅男,一个人出去转不是我的风格. 五一放假之前,坐我对面的一