【经典算法】基本的排序算法:插入排序

引言

An insertion sort starts by considering the two first elements of the array data,which are data[0] and data[1]. if they are out of order, and interchange takes place. Then,the third element, data[2], is considered and inserted into its proper place.If data[2] is less than data[0] and data[1],these two elements are shifted by on position; data[0] is place at position  i, data[1] at position 2,and data[2] at position 0. If data[2] is less than data[1] and not less than data[0],then only data[1] is moved to position 2 and its place is taken by data[2]. If finally, data[2] is not less than both its predecessors,it stays in its current position,Each element data[i] is inserted into its proper location j such the 0<=j<=i, and all elements greater than data[i] are moved by one position.

译: 插入排序首先考虑data中的前两个元素,即data[0]和data[1]。如果它们的次序颠倒了,就交换它们。然后,考虑第三个元素data[2],将其插入到合适的位置上。如果data[2]同时小于data[0]和data[1],那么data[0]和data[1]都要移动一个位置;data[0]放在位置1上,data[1]放在位置2上,而将data[2]放在位置0上。如果data[2]小于data[1],而不于data[0],那么只须把data[1]移动到位置2上,并由data[2]占据它的位置。最后,如果data[2]不小于前两个元素,它将保留在当前的位置。每一个元素data[i]都要插入到合适的位置j上,使0<=j<=i,所有比data[i]大的元素都要移动一个位置。

插入排序算法伪码与实现

插入排序算法的要点如下:

insertionsort(data[],n){
  for(i=1; i<n; i++)
     move all elements data[j] greater than data[i] by one position;
     place data[i] in its proper position;
}

注意,在每次迭代中,排序只限于数组的一小部分,并且只在最后一次迭代中才涉及到整个数组。如下图所示是当对数组[5 2 3 8 1]行insertionsort()方法时,数组所发生的一系变动。

因数组中只有一个元素已排序,所以算法从第二元素位置即位置1开始排序。接着,对于每一个元素tmp = data[i],所有比tmp大的位素都要复制到下一位置上,而把tmp放在合适的位置上。

实现代码如下:

template< class T>
void insertionsort(T data[], int n){
        for(int i =1, j; i<n; i++){
            T tmp = data[i];
            for(j =i; i>0&&tmp< data[j-1]; j--)
                 data[j] = data[j-1];
            data[j] =tmp;
        }
}

插入排序的一个优点是只有需要时才对数组排序。如果数组已经排好序了,就不再对数组进行移动;只有变量tmp得到初始化,存储在变量中的值返回值原位置。可以识别出已排序的数组部分,并相应停止,但忽视元素可以已在合适位置上的事实。一个缺点是:如果插入数据项,所有比插入元素大的元素都必须移动。插入操作不是局部的,可能需要移动大量的元素。

算法分析

为了获取insertionsort()算法执行的移动和比较次数,首先要注意,外层的for语句执行n-1次。但是,比data[i]大的元素移动一个位置的次数并不总是相同的。

最好的情况是:数据已排好序。对于每个位置i,只需比较一匀,这样就只需进行n-1次比较(其数量级是o(n))和2(n-1)次移动,所有的移动和比较都是多余的。最坏的情况是:数据按相反顺序排序。在这种情况下,对于外层for的每次迭代i,都比较i次,即所有迭代的比较总数是


内层for中的赋值次数可以用相同的公式计算。把tmp在外层for中加载和卸载的次数加起来,得到总的移动次数:

前面我们只考虑了极端情况。如果数据是随机排序的,结果如何呢?假设数据项占用数组单元的概率相等,在外层for的迭代i中,data[i]与其它元素的平均比较次数计算如下:

为了求得所有比较次数的平均值,对于所有的迭代i来说,都必须从1开始一直累加到n-1为止。结果是


它的时间复杂度是O(n^2),大约是最坏情况下的比较次数的1/2。按照相同的方式,可以确定在外层for的迭代i中,data[i]需要移动0,1,..i-1次:即

次,加上一定分会发生的两次移动。因此,在外层for中所有迭代中,平均移动次数为

其中时间复杂度为O(n^2)。

综上所述,对于随机顺序的数组来说,移动和比较的次数是与最好情况接近,还是与最坏情况接近?遗憾的是,它接近于后者,即:当数组的元素加倍时,平均需要付出4倍的努力来排序。

参考文献

[1] Adam Drozdek , Data Structures and Algorithms in C++,Third Edition.

=========================================================

转载请注明出处:http://blog.csdn.net/ songzitea/article/details/8864084
=========================================================
原文地址:https://www.cnblogs.com/riskyer/p/3281358.html