排序之直接插入排序
插入排序的基本思想是:每次将一个待排序的元素,按其大小插入到已经排好序的子序列中的适当位置,直到全部元素插入完成为止,下面先来介绍一下直接插入排序。
直接插入排序的思路是:假设待排序的元素放在数组a[0..n-1]中,排序过程中的某一时刻,a被划分为两个子区间a[0…i-1]和a[i…n-1](刚开始时i=1,有序区只有a[0]一个元素),其中,前一个子区间是已经排好序的有序区,后一个区间则是当前未排序的部分,称其为无序区。直接插入排序的一趟操作是将当前无序区的开头元素a[i](1<=i<=n)插入到有序区a[0..i-1]中适当的位置,使a[0….i]变为新的有序区,过程如下所示,该方法称为增量法,因为它每趟操作使有序区增加一个元素。
有序区 无序区
开始时: { a[0]…..a[i-1]} { a[i]….a[n-1] }
一趟排序后: { a[0]…...a[i-1]a[i]} { a[i+1]….a[n-1] }
下面先来说一下直接插入排序的过程:直接插入排序由两重循环构成,对于具有n个元素的序列,外循环要进行n-1趟排序,把所有的元素均插入到序列中。在每一趟排序中,仅当待插入的元素a[i]的值大于等于a[i-1]的值时(亦即此时a[i]的值大于序列a[0….i-1]的所有的值,才无需进入内循环。内循环的作用是把待插入的元素插入到合适的位置上,可以使用元素后移或者元素的交换来实现。下面先给出根据这一思路得到的最直接的代码,然后再给出其优化,为了方便阅读,有些重要语句的意思直接在代码中给出,就不再另写了。
//先判断出要插入的位置,再进行移动覆盖
void Sort_insert(int *a, int n)
{
inti,j,k;
for(i=1;i<n;i++)
{
for(j=i-1;j>=0;j--)
{
if(a[j]<a[i])
break;
}
//找到比a[i]大的最小位置,结果再减去1(j--),也就是满足a[j]<a[i]的最大j值
//所以下面进行数据移动时,K>j,因为a[j]<a[i],不应该参加移动
if(j!= i-1)
{
inttemp = a[i];
for(k=i-1;k>j;k--)
a[k+1]= a[k];
a[k+1]= temp;//把a[i]的值放到正确顺序的位置上
}
}
简化后的代码:
//不是事先找到合适的位置,而是边比较边移动
//不使用移动覆盖,用swap函数交换比较简洁
//不过弱化了直接插入的概念,和冒泡排序法有相似之处
//exchange numbers
void swap(int &Num1, int &Num2)
{
inttemp;
temp= Num1;
Num1= Num2;
Num2= temp;
}
//sort
void Sort_insert(int *a, int n)
{
inti,j;
for(i=1;i<n;i++)
for(intj=i-1; j>=0 && a[j]>a[j+1]; j--)
swap(a[j],a[j+1]);
}
直接插入排序在序列最初为正序时,算法的时间复杂度为最好的情况,为O(n),在最初的序列为反序时,算法的最坏时间复杂度为O(n^2),算法的平均时间复杂度为O(n^2),另外直接插入排序属于就地排序算法。