数据结构复习之排序与直接插入排序 希尔排序

排序基本概念

所谓排序,就是要整理文件中的记录,使之按关键字递增(或递减)次序排列起来。

稳定性

若存在多个关键字相同的记录,经过排序后这些具有相同关键字的记录之间的相对次序保持不变,该排序方法是稳定的
注:
排序算法的稳定性是针对所有输入实例而言的。即在所有可能的输入实例中,只要有一个实例使得算法不满足稳定性要求,则该排序算法就是不稳定的。

分类

内外排序(是否涉及数据的内、外存交换分)

在排序过程中,若整个文件都是放在内存中处理,排序时不涉及数据的内、外存交换,则称之为内部排序(简称内排序);反之,若排序过程中要进行数据的内、外存交换,则称之为外部排序。
注意:
① 内排序适用于记录个数不很多的小文件
② 外排序则适用于记录个数太多,不能一次将其全部记录放入内存的大文件。(不涉及)

按策略分的内排序

五类:插入排序、选择排序、交换排序、归并排序和分配排序。
五类算法的划分,详见:数据结构复习之归并排序与分配排序 总结中的总结部分

评价

好坏标准

① 执行算法需要的时间
② 算法所需的辅助空间
③ 算法本身的复杂程度

算法时间开销

一般情况下,用算法执行中关键字之间的比较次数和记录的移动次数来衡量。

算法空间复杂度

若排序算法所需的辅助空间并不依赖于问题的规模n,空间复杂度是O(1),则称这样的排序为就地排序。非就地排序的空间复杂度是一般为O(n)。

数据结构定义

#define n l00         //假设的文件长度,即待排序的记录数目
typedef int KeyType;  //假设的关键字类型
typedef struct        //记录类型
{
    KeyType key;         //关键字项
    InfoType otherinfo;  //其它数据项,类型InfoType依赖于具体应用而定义
} RecType;
typedef RecType SeqList[MAXSIZE + 1];  // SeqList为顺序表类型,表中第0个单元一般用作哨兵
sqlist R;    // R 顺序表类型数组

插入排序

每次将一个待排序的记录,按其关键字大小插入到前面已经排好序的子文件中的适当位置,直到全部记录插入完成为止。插入排序主要包括:直接插入排序和希尔排序。

直接插入排序

思想

假设待排序的记录存放在数组R[1…n]中,初始时,R[1]自成1个有序区,无序区为R[2…n]。依次将R[i]插入当前的有序区R[1…i-1]中,生成含n个记录的有序区。

算法描述

void InsertSort(Seqlist R,int n)
{
    //对顺序表R做直接插入排序
    int i,j;
    for (i = 2; i <= n; i++)
        if (R[i].key < R[i - 1].key)  //若R[i].key< R[i-1],移动
        {
            R[0] = R[i];                               //当前记录复制为哨兵
            for (j = i - 1; R[0].Key < R[j].key; j--)  //找插入位置,从后向前比较 ,直到减到j=哨兵位置或不小于排好队的最后一个
                R[j + 1] = R[j];                       //也是从后往前移动有序区记录,后移
            R[j + 1] = R[0];                           // R[i]插入到正确位置
        }
}

算法分析

哨兵作用

算法中引进的附加记录R[0]称为哨兵(Sentinel), 哨兵有两个作用:
① 进入查找(插入位置)循环之前,它保存了R[i]的副本,不致于因记录后移而丢失R[i]的内容;
② 它的主要作用是:在查找循环中"监视"下标变量j是否越界。一旦越界(即j=0),因为R[0].key和自己比较,循环判定条件不成立使得查找循环结束,从而避免了在该循环内的每一次均要检测j是否越界(即省略了循环判定条件"j>=1"),使得测试查找循环条件的时间大约减少了一半。

性能分析
  • 时间复杂度
    对于具有n个记录的文件,要进行n-1趟排序。在这里插入图片描述

  • 算法的空间复杂度分析
    算法所需的辅助空间是一个监视哨,辅助空间复杂度S(n)=O(1)。是一个就地排序。

  • 直接插入排序是稳定的排序方法。

希尔排序

思想

先取一个小于n的整数d1作为第一个增量,把文件的全部记录分成d1个组。所有距离为dl的倍数的记录放在同一个组中。先在各组内进行直接插入排序;然后,取第二个增量d2<d1重复上述的分组和排序,直至所取的增量dt=1(dt<dt-l<…<d2<d1),即所有记录放在同一组中进行直接插入排序为止。该方法实质上是一种分组插入方法。

算法实现

void shellsort(seqList R, int d[], int t, int n)
{  //按增量序列d[0…t-1]对顺序表R作希尔排序
    int k;
    for (k = 0; k < t; k++) ShellInsert(R, d[k], n);
}

void ShellInsert(Seqlist R, int dk, int n)
{  //希尔排序中的一趟插入排序,dk为当前增量
    int i, j;
    for (i = dk + 1; i <= n; i++)  //将R[dk+1…n]分别插入有序区
        if(R[i].key < R[i - dk].key) {//以下就和直接插入排序一样了
            R[0] = R[i];  //暂存在R[0]中
            j = i - dk;
            while (j > 0 && R[0].key < R[j].key) {
                R[j + dk] = R[j];  //记录后移,查找插入位置
                j = j - dk;        //查找前一记录
            }
            R[j + dk] = R[0];  //插入R[i]到正确位置
        }
}

算法分析

增量序列选择

① 最后一个增量必须为1;
② 应该尽量避免序列中的值(尤其是相邻的值)互为倍数的情况。

时间复杂度

Shell排序的时间性能优于直接插入排序,实验表明,当n较大时,比较和移动的次数约在nl.25到1.6n1.25之间。

空间复杂度

算法空间复杂度为O(1)。是一个就地排序。

稳定性

时间复杂度

Shell排序的时间性能优于直接插入排序,实验表明,当n较大时,比较和移动的次数约在nl.25到1.6n1.25之间。

空间复杂度

算法空间复杂度为O(1)。是一个就地排序。

稳定性

希尔排序是不稳定的。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

guangod

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值