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