插入排序的几种算法

来源:互联网 发布:教育改革的必要性 知乎 编辑:程序博客网 时间:2024/06/04 17:54

1.直接插入排序
直接插入排序是一种最简单的排序方法,它的基本操作是将一个记录插入到已排好序的有序表中,从而得到一个新的、记录数增1的有序表。
一般情况下,第i趟直接插入排序的操作为:在含有i-1个记录的有序子序列r[1…i-1]中插入一个记录r[i]后,变成含有i个记录的有序子序列r[1…i];并且,和顺序查找类似,为了在查找插入位置的过程中避免数组下标出界,在r[0]处设置监视哨。在自i-1起往前搜索的过程中,可以同时后移记录。直接插入的时间复杂度为O(n²)。

void InsertionSort(Elem R[] , int n){    //对R[1]-R[n]做直接插入排序,R[0]做监视哨    int i,j;    for(i=2;i<=n,i++){        if(R[i].key<R[i-1].key){            R[0]=R[i];     //复制到监视哨            for(j=i-1;R[j].key>R[0].key;--j){                R[j+1]=R[j];   //元素后移            }            R[j+1]=R[0];   //插入到正确位置        }    }}

2.折半插入排序
由于插入排序的基本操作就是在一个有序表中进行查找和插入,这个“查找”可以利用“折半查找”来实现,由此进行的插入排序称之为折半插入排序,算法如下。

void BInsertSort(Elem R[] , int n){    int i,j;    for(i=2;i<=n,++i){        if(R[i].key<R[i-1].key){            R[0]=R[i];     //复制到监视哨            //折半查找插入位置            //查找到相等继续查找到插入为止,最后插入位置一定为high+1            int low=1;            int high=i-1;            while(low<=high){                int m = (low+high)/2;                if(R[m].key<R[0].key){                    low=m+1;                }else{                    high=m-1;                }            }            for(j=i-1;j>=high+1;--j){                R[j+1]=R[j];   //元素后移            }            R[j+1]=R[0];   //插入到正确位置        }    }}

折半插入排序所需附加存储空间和直接插入排序相同,从时间上比较,折半插入排序仅减少了关键字间的比较次数,而记录的移动次数不变,因此时间复杂度仍为O(n²)。
3.表插入排序

 typedef struct {   RedType rc; /* 记录项 */   int next; /* 指针项 */ }SLNode; /* 表结点类型 */ typedef struct {   SLNode r[SIZE]; /* 0号单元为表头结点 */   int length; /* 链表当前长度 */ }SLinkListType; /* 静态链表类型 */ void TableInsert(SLinkListType *SL,RedType D[],int n) { /* 由数组D建立n个元素的表插入排序的静态链表SL */   int i,p,q;   (*SL).r[0].rc.key=INT_MAX; /* 表头结点记录的关键字取最大整数(非降序链表的表尾) */   (*SL).r[0].next=0; /* next域为0表示表尾(现为空表,初始化) */   for(i=0;i<n;i++)   {     (*SL).r[i+1].rc=D[i]; /* 将数组D的值赋给静态链表SL */     q=0;     p=(*SL).r[0].next;     while((*SL).r[p].rc.key<=(*SL).r[i+1].rc.key)     { /* 静态链表向后移 */       q=p;       p=(*SL).r[p].next;     }     (*SL).r[i+1].next=p; /* 将当前记录插入静态链表 */     (*SL).r[q].next=i+1;   }   (*SL).length=n; }

表插入排序的时间复杂度仍为O(n²)。
4.希尔排序
希尔排序又称为“缩小增量排序”,时间效率较前面几种方法有很大改进。
直接插入排序为“正序”的时候,时间复杂度为O(n),由此可设想,待排序记录序列关键字“基本有序,直接插入排序的效率就大大提高;从另一方面看,由于直接插入排序算法简单,则在n值很小时效率比较高。希尔排序正是从这两点分析出发对直接插入排序进项改进得到的一种排序算法。
它的基本思路是:先将整个待排记录序列分割成若干个子序列分别进行直接插入排序,待整个序列中的记录”基本有序“时,在对全体记录进行一次直接插入排序。
特点是:子序列的构成不是简单地逐断分割,而是相隔某个”增量“的记录组成一个子序列,最后在进行一次增量是1的插入排序(等同于直接插入排序),此时序列以基本有序,只需要进行少量的比较和移动即可完成排序。

void  ShellInsert(ElemType R[] , int dk){    //对顺序表R做一趟希尔插入排序。本算法与一趟直接插入排序,做了一下修改    //1.前后记录位置的增量是dk,而不是1    //2.R[0]只是暂存单元,不是哨兵。当j<=0时,插入位置已经找到    int i,j;    for(i=dk+1;i<n;1++){        if(R[i].key<R[i-1].key){    // 需将R[i]插入到有序增量子表            R[0]=R[i];              // 暂存在R[0]            for(j=i-dk;j>0&&(R[j].key>R[0].key);j-=dk){                R[j+dk]=R[j];       //记录后移,查找插入位置            }            R[j+dk]=R[0];           //插入        }    }}void ShellSort(ElemType R[] ,int dlta[], int t){    //按增量序列dlta[0...t-1]对顺序表R做希尔排序    int k;    for(k=0;k<t;k++){        ShellInsert(R,dlta[k]);     //一趟增量为dlta[k]的插入排序    }}
0 0