插入排序

来源:互联网 发布:云计算专业认识 编辑:程序博客网 时间:2024/05/24 00:36

中心思想:考虑你正在斗地主,对方发给你一堆牌,你右手摸牌,左手拿牌,第一次摸到10放到左手,第二次摸到3就放到10左边,第三次摸到K放10右边,左手上的牌一直保持有序,直到左手拿着所有的牌为止。这就是插入排序。
插入排序从第二个元素开始,在前面已排好序的序列中寻找合适的位置并插入,使之仍然有序。
插入排序最多的步骤在于移位操作,每个元素插入之前,数组的大于待插入值的元素必须事先挪位,故插入排序在最坏情况下是o(n*n)的复杂度。

直接插入:

//插入排序,不支持参数检查void insert_sort(int * a , int n){    int i,j,key;    for(i=1;i<n;++i)    {        key = a[i];//key 为待插入的元素        //挪位        for( j = i-1;j>=0 && a[j] >key;--j)        {            a[j+1] = a[j];//两重循环,故O(n*n)        }        //在合适的位置插入        a[j+1] = key;    }}

在一万个数据的运行结果:
数据随机生成,循环20次得到
插入排序有个挪位操作,每次循环必须判断移动的元素是否大于待插入的元素,为了省去不必要的判断,我们可以先用二分法寻找合适的插入位置。

折半插入:

void insert_sort(int * a , int n){    int i,j,key,low,high,mid;    for(i=1;i<n;++i)    {        key = a[i];        //二分查找运算        low = 0,high = i-1;        while(low <= high)        {            mid = (low + high)/2;            if(key > a[mid])                low = mid + 1;            else// if(key <= a[mid])                high = mid - 1;        }        //挪位        for( j = i-1;j >= low;--j)        {            a[j+1] = a[j];        }        a[j+1] = key;    }}

在一万个数据的运行结果:
可以看出快了大概十多毫秒
结果快了大概十多毫秒。。。我想应该是移位操作步数并没有因此减少,只是对比较操作优化了一下而已。所以效果并不明显。

二路插入:

那么有没有办法减少移位的步数呢?仔细一想,有一种情况是不需要移位的,那就是待插入的元素刚好插入到有序序列的最后。我们想一下,如果有序序列前面还有位置放会怎样?本来需要把所有有序序列移位的现在也变成不需要移位了。那么如何实现呢?我们可以利用循环数组的想法来做。用first表示有序序列的开始,last表示有序序列的结尾,如果待插入元素小于first值或者大于last值,就放到外面去;如果待插入元素刚好处于first值和last值之间,那么就插入到有序序列中间,这和直接插入一样。
举例:如果有序序列是 1 2 3 5,待插入的是0,那么我就把它放到数组的最后,让first等于最后一个(first = n -1),让last = 2。
二路插入的思想是以空间换取时间的想法。


void insert_sort(int * a , int n){    int *t;    t = new int [n];    int i,j,key,first=0,last=0;    t[0] = a[0];    for(i=1;i<n;++i)    {        key = a[i];//key 为待插入的元素        //如果待插入元素比first值还小        if(key <= t[first])        {            first = (first-1+n)%n;            t[first] = key;        }        //如果待插入元素比last值还大        else if(key >= t[last])        {            last = last + 1;            t[last] = key;        }        //如果待插入元素处于first和last之间        else //if(key > t[first] && key < t[last])        {            //挪位            for( j = last;j != first && t[j] > key; j= (j-1+n)%n)            {                t[(j+1)%n] = t[j];//两重循环,故O(n*n)            }            //在合适的位置插入            t[(j+1)%n] = key;            last = (last + 1)%n;        }    }    //赋值给a数组    j=0;    i =first;    do{         a[j++] = t[i];         i = (i+1)%n;    }while(i != (last + 1)%n);}

但是运行结果却出乎意料:
这里写图片描述
但是对于完全逆序的数据却出乎意料的快:
这里写图片描述
经过仔细分析,原来是取余(循环链表需要用到模运算)造成的影响,于是修改一下:

void insert_sort(int * a , int n){    int *t;    t = new int [n];    int i,j,key,first=0,last=0;    t[0] = a[0];    for(i=1;i<n;++i)    {        key = a[i];//key 为待插入的元素        //如果待插入元素比first值还小        if(key <= t[first])        {            first = (first-1+n)%n;            t[first] = key;        }        //如果待插入元素比last值还大        else if(key >= t[last])        {            t[++last] = key;        }        //如果待插入元素处于first和last之间        else //if(key > t[first] && key < t[last])        {            //挪位            int k1,k2;            for( j = last;j != first && t[j] > key; j= (!j)?(j-1+n):j-1)//避免取余带来的性能损耗            {                k1= (!(j-n+1))?j+1-n:j+1;                t[k1] = t[j];//两重循环,故O(n*n)            }            //在合适的位置插入            t[(!(j-n+1))?j+1-n:j+1] = key;            ++last;        }    }    //赋值给a数组    j=0;    i =first;    do{         a[j++] = t[i];         i = (!(i-n+1))?i+1-n:i+1;    }while(i != last + 1);}

运行结果:
这里写图片描述
我只能说我已经尽力了,对于均匀随机产生的数据,二路插入只有70多毫秒,比直接插入还要慢一点,但是对于极端情况即完全逆序或者完全升序的情况来说是很快的,权衡一下应该选择二路插入好一点。

希尔排序:

为了省略移位带来的影响,希尔提出了这样一种思路:如果待排序的数组元素相对有序,那么是否会降低移位操作的频率?
如果在插入元素之前,数组都相对有序,大的元素都在后头,小的元素都在前面,这样只需移动少许的元素就可以使数组升序。
上代码:

void insert_sort(int * a , int n){    int i,j,key,step;//step 为步长    step = n >>1;    while(step!=0)    {        //cout<<step<<endl;        for(i=0;i<n;i += step)        {        key = a[i];//key 为待插入的元素        //挪位        for( j = i-step;j>=0 && a[j] >key;j -= step)        {            a[j+step] = a[j];//两重循环,故O(n*n)        }        //在合适的位置插入        a[j+step] = key;        }        step = step >>1;    }}

我感觉希尔排序跟直接插入排序的代码差不多,不同的是下标值的变换,希尔排序会依次缩小步长,增大待排序的元素,直到步长为1,即变成了直接插入排序,但由于之前的排序是数组相对有序(大小错落有致),所以移位运算大大减少,因此性能得到提高:
这里写图片描述
以上,便是插入排序的大致内容,这是我的第一篇博客,谢谢观看。

1 0
原创粉丝点击