直接插入排序的优化


直接插入排序是最简单的一种排序算法,其基本思想不必赘述。每个元素插入到队列中经过两个步骤:首先是依次比较,找到自己在已经有序的子列上的的位置,其次是把后面的数据全部移位,然后把元素插入。所以,优化直接排序也就有了两种思路:第一,减少查找时间;第二,减少移位时间。不经优化的直接插入排序,查找过程的复杂度是O(n^2),移位过程的复杂度也是O(n^2)。     

 这里重点强调一下,在直接插入排序中,使用哨兵位,在序列为数组且长度LENGTH为10000的规模下,能减少10%左右的时间。是一个非常实用的技巧。

 代码对比如下: 

 //没有哨兵位的直接插入排序
void sort(int a[]){
    int i;
    int j;
    int t;
    for(i=2;i<LENGTH;i++){
        t=a[i];
        j=i-1;
        while(j>0 && a[j]>t){
            a[j+1]=a[j];
            j--;
        }
        a[j+1]=t;
    }
}

 

//通过设置哨兵位,减少10%左右时间
void sort1(int a[]){
    int i;
    int j;
    int t;
    for(i=2;i<LENGTH;i++){
        t=a[i];
        j=i-1;
        a[0]=t;//哨兵位
        while(a[j]>t){
            a[j+1]=a[j];
            j--;
        }
        a[j+1]=t;
    }
}

 

 (一)减少查找次数   

减少查找时间,一种变形就是变顺序查找为折半查找,即变形为折半插入排序,把查找所用的时间从O(n^2)降低到O(nlogn),在LENGTH=100000的规模下,能继续优化10%的时间消耗。

代码实现如下:

//折半插入排序
void sort(int a[]){
    int i,j,low,high,m;
    for(i=2;i<LENGTH;i++){
        a[0] = a[i];         //哨兵位
        low=1;
        high=i-1;
        while(low<=high){
            m =  (low+high)/2;
            if(a[i]<a[m]){
                high = m-1;
            }else{
                low = m+1;
            }
        }
        for(j=i-1;j>=high+1;j--){
            a[j+1] = a[j];
        }
        a[j+1] = a[0];
    }
}

(二)减少移位次数

首先想到的是改变数据结构,使用链表代替数组,可以把移位访存的复杂度从O(n^2)降至常数级O(1)。为了查找方便,使用双向链表结构。

全程序代码如下:

 

/*
  Link Insertion Sort
*/
#include<iostream>
#include<cstdlib>
#include<ctime>
using namespace std;
const int LENGTH=100000;
typedef struct list{
    int data;
    struct list *pre,*next;
}A;

//排序算法
void sort(A *h,A *r){
    if(h==NULL)
        return;
    A *p = h->next;
    A *q = p->next;
    if(p==r||q==r)
        return;
    int t;
    while(q != r){
        t=q->data;
        h->data = t;
        int f=0;
        while(p->data > t ){
            p = p->pre;
            f=1; //交换结点位置 1
        }
        if(f==1){
            //把 q 插入到 p 后面
            A *qNext = q->next;
            A *qPre = q->pre;
            A *pNext = p->next;
            //移除 q
            qPre->next = qNext;
            qNext->pre =  qPre;
            //添加 q
            p->next = q;
            q->pre = p;
            q->next = pNext;
            pNext->pre = q;
            //复位 p q
            q = qNext;
            p = qPre;
        }else{
            p = q;
            q = q->next;
        }
    }
}
void main(){
     A *h = new A(); //头结点
     h->pre = NULL;
     h->next = NULL;
     h->data = 0;
     A *p = h;
     int i;
     for(i=0;i<LENGTH;i++){
          p->next = new A();
          p->next->pre = p;
          p->next->data = rand()%LENGTH;//LENGTH-i;    最差与随机情况
          p->next->next = NULL;
          p = p->next;
     }
     A *r = new A(); //尾结点
     p->next = r;
     r->pre = p;
     r->next = NULL;
/*   遍历
     p=h->next;
     cout<<"排序前"<<endl;
     while(p!=r){
          cout<<p->data<<" ";
          p = p->next;
     }
     cout<<endl;
*/
     //计算时间
     clock_t start, end;
     start = clock();
     sort(h,r);
     end = clock();
/*   遍历
     cout<<"排序后"<<endl;
     p=h->next;
     while(p!=r){
          cout<<p->data<<" ";
          p = p->next;
     }
     cout<<endl;
*/
     cout<< (long int)(end - start)<<endl;
}


但是经过实际测量,链表插入排序的时间竟然有数组直接插入排序的8倍之多。个人分析原因,首先一点是因为链表的数据结构,



 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值