您的位置:首页 > 其它

【直接插入排序】和【希尔排序】

2017-07-13 16:23 169 查看

一、直接插入排序

(1)算法步骤(升序)

1>将待排序数组的第一个数拿出来看做一个已经排序好的有序区间;将后面的数列看做一个无序的待排序数列;

2>从有序数列的最后一个数开始和待排序数列的第一个数key比较(即有序数列的最后一个位置的下一个位置的数),如果有序数列的最后一个数大于key,则将有序数列的最后一个数填到key的位置;然后取有序数列的前一个数和key比较,大于key则填入后一个位置,继续取有序数列前面数和key比较;直到 发现比key小的数据;将key填入该数据的后面的位置;

3>重复步骤2,直到无序数列没有数据,排序完成

——动图展示



(2)图说



(3)代码实现

#include<iostream>
#include<assert.h>
using namespace std;

void InsertSort(int* arr,int sz)//sz为最后一个数的数组下标
{
assert(arr);
for(int i=0;i<sz;i++)//i代表有序区间的末尾地址,最后只能到整个区间的倒数第二个位置
{
int end=i;//记录有序期间的末尾
int key=arr[end+1];//待排序的元素
while (end>=0)//end从后往前走和key比较
{
if (arr[end]>key)
{
arr[end+1]=arr[end];//end+1位置一定放的是比end位置大的数
--end;
}
else
break;
}
//出循环有两种可能,一种end<0(key插入有序区间的末尾),另一种arr[end]<key(key插入有序区间的非末尾);
//出循环后end为key在有序区间的正确位置的前一个位置;
arr[++end]=key;
}
}
void printArray(int* a,int n)//打印数组
{
for(int i=0;i<n;++i)
{
cout<<a[i]<<" ";
}
cout<<endl;

}
int Test()
{
int a[]={9,5,3,8,1,6,7,4};
int sz=sizeof(a)/sizeof(a[0]);
InsertSort(a,sz-1);
printArray(a,sz);
return 0;
}
int main()
{
Test();
return 0;
}


(4)测试结果



(5)时间复杂度&&空间复杂度分析

【1】时间复杂度分析:

假入是把目标按升序排列;那么对于排列N个数的数组;

最坏情况:

排序的数组为降序有序;这时最终排序完成需要比较的次数为1+2+3+…+N-1=(N-1)(N-1-1)/2,所以时间复杂度为O(N^2);

最好情况:

所排序的数组本身就是一个升序有序数组;这时需要比较的总次数为N-1次; 所以此时时间复杂度为O(N);

一般情况: 时间复杂度O(N^2);

【2】空间复杂度:

由于算法需要存储的是有限个数据的存储空间,故此插入排序的空间复杂度为O(1)。

【3】直接插入排序的适用范围

插入排序不适合对于数据量比较大的排序应用。

插入排序适合对数据较少的数组进行排序例如量级小于千;

二、希尔排序

(1)简介

希尔排序为直接插入排序的优化,为了防止以上直接插入排序出现最坏情况的出现影响算法效率,希尔排序对其进行了改进;在对数据进行直接排序之前先对数据进行预排序,使之接近有序,然后再利用直接插入排序对数据进行快速排序。

总的来说,希尔排序是基于插入排序的以下两点性质而提出改进方法的:

(2)基本思想

先将整个待排元素序列分割成若干个子序列(由相隔某个“增量”的元素组成的)分别进行直接插入排序,然后依次缩减增量再进行排序,待整个序列中的元素基本有序(增量足够小)时,再对全体元素进行一次直接插入排序。

(3)图说



(4)代码实现

#include<iostream>
#include<assert.h>
using namespace std;
----------//核心代码

void ShellSort(int* arr,int sz)//sz为最后元素的下标
{
int gap=sz;//记录分割增量
while(gap>1)
{
gap=gap/3+1;//gap依次缩小,让数据更加有序
for(int i=0;i<sz-gap+1;i++)
{
int end=i;//该组有序区间的末尾下标
int key=arr[end+gap];//该组待排序的值
while (end>=0)
{
if(arr[end]>key)
{
arr[end+gap]=arr[end];
end-=gap;
}
else
break;
}
arr[end+gap]=key;
}
}

}

----------

void printArray(int* a,int n)//打印数组
{
for(int i=0;i<n;++i)
{
cout<<a[i]<<" ";
}
cout<<endl;

}
int Test()
{
int a[]={9,5,3,8,1,6,7,4};
int sz=sizeof(a)/sizeof(a[0]);
ShellSort(a,sz-1);
printArray(a,sz);
return 0;
}
int main()
{
Test();
return 0;
}


代码解读:

gap=gap/3+1确保了最后一次进行的是gap=1的直接插入排序;

且每一次循环进入gap的减小,都可以使得一次预排序的结果数据更加的接近有序;

while循环里面的for循环,成功的一次过去将不同的分组中的数据在各自的分组中都进行一次直接插入排序;

测试结果:



(5)时间复杂度&&空间复杂度分析

一般情况:O(N)<希尔排序的时间复杂度

(6)本人对希尔排序的理解

希尔排序在整体上分为两大部分,第一部分为预排序,第二部分为直接插入排序;

预排序是为了可以尽快的将比较小的数放在比较靠前的位置(这里,我们都是按照升序进行叙述的);gap越大,每组最后的数据会更快的来到前面,但是gap越大,每一次预排序完成以后整体上就越不接近有序;所以要依次使gap递减,使得每次预排序完成以后数据更加接近有序;

三、直接插入排序和希尔排序的对比

(1)当数据很多,且数据整体逆序时—-使用希尔排序

(2)当数据逆序,但数据不是很多时—-采用希尔排序和直接插入差不多

(3)当数据基本有序时,且数量不能太大(量级为千级)—–使用直接插入排序
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐