#插入类排序 ##一、直接插入排序
算法分析: 从空间角度来看,只需要一个辅助空间r[0]。 从时间角度来看,主要时间耗费在关键字比较和移动元素上。 直接插入排序的时间复杂度为O(n^2),空间复杂度为O(1)。 直接插入排序是稳定的。 **直接插入排序在基本有序时效率较高,并且在序列规模不是很大时效率也很高。**
void InsertSort(int a[],int len) //直接插入排序
{
int i = 1;
int j = 0;
for(i = 2; i <= len; ++i)
{
a[0] = a[i];
for(j = i-1; j > 0 && a[j] > a[0]; j--)
{
a[j+1] = a[j];
}
a[j+1] = a[0];
}
}
二、二分插入排序
算法分析: 采用折半插入排序法,可减少关键字的比较次数。每插入一个元素,需要比较的次数最大为折半判定树的深度。**虽然折半插入排序法与直接插入排序法相比较,改善了算法中比较次数的数量级为O(nlog2n),但其并未改变移动元素的时间耗费,所以折半插入排序总的时间复杂度仍然是O(n^2)。**
void BinInsertSort(int a[],int len) //二分插入排序
{
int i = 0;
for(i = 2; i <= len; ++i)
{
int low = 1;
int high = i-1;
int mid = 0;
a[0] = a[i];
while(high >= low)
{
mid = (low + high)/2;
if(a[0] <= a[mid])
{
high = mid - 1;
}
else //把等于划到这一类可以在有多个相同值时减少相同值移动的次数;
{
low = mid + 1;
}
}
int tmp = 0;
for(int j = i - 1; j >= low; j--)
{
a[j+1] = a[j];
tmp++;
}
cout<<endl<<i<<"移动%d次:"<<tmp<<endl;
a[low] = a[0];
}
}
上述代码中的逻辑设计的实际区别如下:
(1)把等于划到下面
(2)把等于划到上面
可见,把等于号放在下面的分支,相当于遇到相等元素时、不在进行比较能够提高效率。
三、shell排序
算法分析:shell排序是对直接插入排序的一种改进(从后续的代码中可以看出),延续了直接插入排序的优点,核心思想是: >待排序列有n个元素,先取一个小于n的整数h1作为第一个增量,把待排序列以间隔h1分成若干子序列,子序列内使用插入排序; >然后取第二个增量h2(
void ShellSort(int a[], int len , int d[], int dlen)//shell排序非递归实现
{
int i = 0,j = 0,n = 0;
for(i = 0;i<dlen; ++i) //共有dlen种偏移因子
{
for(n = 1; n < d[i]+1; n++) //共有d[i]组
{
for(j = n+d[i]; j<=len; j +=d[i]) //一组插入
{
a[0] = a[j];
int k = j-d[i];
while(k>0 && a[k] > a[0])
{
a[k+d[i]] = a[k];
k = k-d[i];
}
a[k+d[i]] = a[0];
}
}
}
}
总结:
插入排序 | 时间复杂度 | 稳定性 |
---|---|---|
直接插入排序 | O(n^2) | 稳定 |
二分插入排序 | 比较O(nlog2n)、移动O(n^2) | 稳定 |
shell排序 | O(n^1.5) | 不稳定 |