最近复习《数据结构》,复习到了内部排序,就把书上的算法用代码实现了。课本用的是严慧敏老师主编的那本。插入排序算法算法思想很简单,就是从第二个元素开始把每个元素逐个插入到前面已排好序的有序表中,渐增的实现排序。下面用一个伪代码来理解一下:
InsertSort(A) for m← 2 to length[A] do key ← A[m] n ← m-1 while n>0 and A[n]>key do A[n+1] ← A[n] n ← n-1 A[n+1] ←key
这个算法的时间复杂度是O(n^2)。时间消耗在了元素间的比较和移动上了,如果能减少元素间的比较或者是元素的移动那么就能提高了。后面的文章中用介绍用于减少元素间比较次数的折半插入排序;和用于减少元素的移动的2-路插入排序;以及不需要移动元素的表插入排序。
根据上面的伪代码很容易写出源代码,就像下面的这样:
//直接插入排序 //参数 numbers 是:指向要排序数的数组 //宏定义 COUNT 是:数组中元素的个数 void DirectInsertSort(int* numbers) { int m,n=0; for(m=2;m<COUNT;m++) { int key = numbers[m]; n=m-1; while( n>0 && numbers[n]>key) { numbers[n+1] = numbers[n]; n--; } numbers[n+1] = key; } }如果把存储数的数组的第一个元素用来当哨兵的话,很容易把函数改成如下的形式:
//直接插入排序 //参数 numbers 是:指向要排序数的数组 //宏定义 COUNT 是:数组中元素的个数 void DirectInsertSort(int* numbers) { int i,j=0; for(i=2;i<COUNT;i++) { if(numbers[i] < numbers[i-1]) { // numbers[0] 当哨兵 numbers[0] = numbers[i]; for(j=i-1; numbers[0]<numbers[j];j--) { numbers[j+1] = numbers[j]; } numbers[j+1] = numbers[0]; } } }
哨兵元素的好处:
从上面两段代码就能看出有了哨兵的好处:每次不必判断n>0了,也就是说不用检查数组是否越界了。这在很少数的排序中看不出效果,但当要排列的数很多的时候,
性能就能比较出来了。
下面贴出这次的整个代码,接下来几篇会介绍折半插入排序、2-路插入排序和表插入排序:
#include <iostream> using namespace std; #define COUNT 9 //输出排序后的数组 void PrintValues(int* numbers) { for(int i=1;i<COUNT;i++) { cout<<numbers[i]<<" "; } } //直接插入排序 //参数 numbers 是:指向要排序数的数组 //宏定义 COUNT 是:数组中元素的个数 void DirectInsertSort(int* numbers) { int m,n=0; for(m=2;m<COUNT;m++) { int key = numbers[m]; n=m-1; while( n>0 && numbers[n]>key) { numbers[n+1] = numbers[n]; n--; } numbers[n+1] = key; } /* int i,j=0; for(i=2;i<COUNT;i++) { if(numbers[i] < numbers[i-1]) { numbers[0] = numbers[i]; for(j=i-1; numbers[0]<numbers[j];j--) { numbers[j+1] = numbers[j]; } numbers[j+1] = numbers[0]; } } */ } int main(int argc, char *argv[]) { //第一个元素当哨兵位,不用。 int values[COUNT] = {0, 49, 38, 65, 97, 76, 13, 27, 49}; DirectInsertSort(values); PrintValues(values); return EXIT_SUCCESS; }
结果如下图: