排序之插入排序(直接插入排序,折半插入排序,希尔排序)

1. 直接插入排序

   一种最直观,最简单的排序方式。假设在排序过程中,待排序表L[1…….n]在某次排序过程中的某一时刻状态如下:

有序序列L[1…..i-1]L(i)无序序列L[i+1…….n]
   a.操作步骤如下:

      1) 查找出L[i]在有序序列L[1…….i-1]中的插入位置k(注意,L[i]已经赋值在哨兵位置)
      2)将L[k…….i-1]所有元素全部后移一个位址
      3)将L[i]复制到L[k].

   b.代码
void InsertSort(ElemType A[], int n){
        int i,j;
        for(i=2;i<=n;i++)             //依次将A[2].....A[n]插入到前面已排序序序列
            if(A[i].key<A[i-1].key){  //A[i]的关键码小于前值,则需要排序插入前面  
                A[0] = A[i];      //“哨兵” 
                for(j=i-1;A[0].key<A[j].key;--j)   //从后向前插
                    A[j+1] = A[j];              //向后挪位
                 A[j+1] = A[0];                      //复制到插入位置
            }
    }
   c.性能分析

      空间复杂度O(1). 时间复杂度O(n^2).
      稳定性:每次插入元素总是从后面开始比较,不会出现相同元素相对位置发生变化的情况。是稳定的排序方法。

2. 折半插入排序

表与上面的表相同,故不添上去了。

   a. 基本思想

      上述代码中是边比较边移动元素。而折半查找不同,它是在已排好序的前表找到待插入位置(使用到折半查找),再统一移动待插入位置之后的元素。

   b. 代码
void InsertSort(ElemType A[], int n){
    int i,j,low,high,mid;                  //折半查找时使用的变量出现了
    for(i=2;i<=n;i++){                     //依次将A[2]...A[n]插入前面排好的序列中
        A[0] = A[i];                   //"哨兵"
        low=1;high=i-1;                //折半查找范围
        while(low<=high){              //折半查找
            mid = (low+high)/2;
            if(A[mid].key>A[0].key) high = mid-1;   //查找左表
            else low = mid+1;                       //查找右表
        }
        for(j=i-1;j>=high+1;--j)
            A[j+1] = A[j];                         //统一后移元素。空出插入位置
        A[high+1] = A[0];                              //插入
    }
}
   c. 性能分析

      空间复杂度O(1). 时间复杂度O(n^2)。减少了比较次数O(nlogn),而且比较次数与待排序表的初始状态无关,仅取决于表中的元素个数n。移动次数未改O(n^2)。所以总体来说就是O(n^2)。
      是个稳定的排序方法。

3. 希尔排序

   a. 基本思想

       直接额插入排序适用于基本有序和数据量不大的排序表。希尔排序是在直接插入排序的基础上改进,先将待排序表分割成若干个形如L[i,i+d,i+2d,……,i+kd]的“特殊”子表,分别进行直接插入排序。待基本有序后,再对全体记录进行一次直接插入排序。
      实现步骤:先取一个小于n的步长d1,把表中全部记录分为d1个组,所有距离为d1的倍数的记录放在同一个组中,在各组中进行直接插入排序,然后取第二个步长d2

   b. 代码
 void ShellSort(ElemType A[], int n){
         //对顺序表作希尔插入排序。与直接插入排序相比,作了2处修改
         //1. 前后记录位置的增量是dk,不是1。 2. r[0]只是暂存单元,不是“哨兵”
         for(dk=n/2;dk>=1;dk=dk/2)                  //步长变化
             for(i=dk+1;i<=n;++i)
                 if(A[i].key<A[i-dk].key){   //需将A[i]插入有序增量子表
                     A[0]=A[i];          //暂存在A[0]
                     for(j=i-dk;j>0&&A[0].key<A[j].key;j-=dk)
                        A[j+dk] = A[j];    //记录后移,查找插入的位置
                    A[j+dk] = A[0];            //插入
                }//if
     }
   c. 性能评估

      空间复杂度易从代码看出O(1)
      时间复杂度O(n^2)
      稳定性:不稳定。例:[3, #2, 2] -> [2, #2, 3] 其中2的相对位置发生了改变。
      仅适用于当线性表为顺序存储的情况。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值