【数据结构】-内部排序(插入排序)

秒速五厘米 2022-11-25 11:49 262阅读 0赞

内部排序-插入排序

  • 写在前面
  • 1.头文件及类型定义
  • 2.函数声明
  • 3.基本操作
    • 3.1 直接插入排序
    • 3.2 折半插入排序
    • 3.3 希尔排序
  • 4.main函数
  • 5.小结

写在前面

【说明】以下代码实现最终均为递增序列,即从小到大排序。

1.头文件及类型定义

  1. #include<stdio.h>
  2. #define ElemType int

2.函数声明

  1. /*函数声明*/
  2. void DirectInsertSort(ElemType A[], int n); //1.直接插入排序
  3. void HalfInsertSort(ElemType A[], int n); //2.折半插入排序
  4. void ShellSort(ElemType A[], int n); //3.希尔排序

3.基本操作

3.1 直接插入排序

  1. //1.直接插入排序
  2. void DirectInsertSort(ElemType A[], int n) {
  3. int i, j;
  4. for(i=2;i<=n;i++) //依次将A[2]~A[n]插入到前面已排序序列
  5. if (A[i] < A[i - 1]) {
  6. //若A[i]关键字小于其前驱,将A[i]插入有序表
  7. A[0] = A[i]; //A[0]设置为“哨兵”
  8. for (j = i - 1; A[0] < A[j]; j--) //从后往前顺序查找待插入位置
  9. A[j + 1] = A[j]; //向后挪位
  10. A[j + 1] = A[0]; //插入指定位置
  11. }
  12. }

3.2 折半插入排序

  1. //2.折半插入排序
  2. void HalfInsertSort(ElemType A[], int n) {
  3. int i, j, low, high, mid;
  4. for (i = 2; i <= n; i++) {
  5. //依次将A[2]~A[n]插入到前面已排序序列
  6. A[0] = A[i]; //A[i]暂存到A[0]
  7. low = 1; //设置折半查找的范围
  8. high = i - 1;
  9. while (low <= high) {
  10. //折半查找(默认递增有序)
  11. mid = (low + high) / 2; //取中间点(向下取整)
  12. if (A[mid] > A[0])
  13. high = mid - 1; //查找左子表
  14. else
  15. low = mid + 1; //查找右子表
  16. }
  17. for (j = i - 1; j >= high + 1; j--)
  18. A[j + 1] = A[j]; //统一后移元素,空出插入位置
  19. A[high + 1] = A[0]; //插入指定位置
  20. //或者 A[low] = A[0]; 因为最后是在low>high时退出循环的,故low=high+1
  21. }
  22. }

3.3 希尔排序

  1. //3.希尔排序
  2. void ShellSort(ElemType A[], int n) {
  3. int d, i, j;
  4. //A[0]只是暂存单元,不是哨兵,当j<=0时,插入位置已到
  5. for (d = n / 2; d >= 1; d /= 2) //步长(增量)变化
  6. for (i = d + 1; i <= n; i++)
  7. if (A[i] < A[i - d]) {
  8. //需将A[i]插入有序增量子表
  9. A[0] = A[i]; //暂存在A[0]
  10. for (j = i - d; j > 0 && A[0] < A[j]; j -= d)
  11. A[j + d] = A[j]; //记录后移,查找插入位置
  12. A[j + d] = A[0]; //插入指定位置
  13. }
  14. }

4.main函数

  1. int main() {
  2. //1.直接插入排序
  3. ElemType A[11];
  4. for (int i = 1; i < 11; i++)
  5. scanf("%d", &A[i]);
  6. DirectInsertSort(A, 10);
  7. for (int i = 1; i < 11; i++)
  8. printf("%d\t", A[i]);
  9. printf("\n");
  10. //2.折半插入排序
  11. ElemType B[11];
  12. for (int i = 1; i < 11; i++)
  13. scanf("%d", &B[i]);
  14. HalfInsertSort(B, 10);
  15. for (int i = 1; i < 11; i++)
  16. printf("%d\t", B[i]);
  17. printf("\n");
  18. //3.希尔排序
  19. ElemType C[9];
  20. for (int i = 1; i < 9; i++)
  21. scanf("%d", &C[i]);
  22. ShellSort(C, 8);
  23. for (int i = 1; i < 9; i++)
  24. printf("%d\t", C[i]);
  25. return 0;
  26. }

5.小结

一、插入排序的概念

  • 顾名思义,就是插入(hhh),主要分两步:
    ① 找插入位置(比较):得知道往哪插,就是查找咯,用顺序查找就是直接插入排序,用折半查找就是折半插入排序,所谓查找也就是比较的过程。
    ② 插入(移动):和按个子大小站队一样,找到插入位置以后,你先从队里面出来,跑到要插入的位置,别人向后移动,填补你原来的位置。

二、关于三种插入排序的关系

逐渐优化:直接插入排序——>折半插入排序——>希尔排序
[解释]
给定一个乱序序列,从第2个元素开始,依次和前面元素比较(即顺序查找),也就是进行直接插入排序。因此,每一趟直接插入排序开始时,它前面的序列都是有序的(因为前面的已经比过了)。
既然前面的元素有序,则可以折半查找来提高查找插入位置的效率,也就是折半插入排序
试想:如果给定序列本身就由小到大,那么则只需要从第2个元素开始比较,不需要移动,由此可见直接插入排序更适用于基本有序且数据量不大的排序表,希尔排序就是基于这两点分析进一步改进的,由部分有序到整体有序。

三、关于三种插入排序的性能分析

  1. 直接插入排序
    空间复杂度:O(1)
    时间复杂度:O(n2)
    稳定性:稳定
    适用性:适用于顺序存储和链式存储的线性表
  2. 折半插入排序
    空间复杂度:O(1)
    时间复杂度:O(n2)
    稳定性:稳定
    适用:仅适用于顺序存储的线性表
  3. 希尔排序(缩小增量排序)
    空间复杂度:O(1)
    时间复杂度:最坏为O(n2)
    稳定性:不稳定
    适用:仅适用于顺序存储的线性表
    【注】:当增量d=1时,希尔排序会退化为直接插入排序

四、其他说明

  1. 对直接插入排序和折半插入排序来说,每一趟排序都会得到一个部分有序的序列,这可以作为判断是否进行了直接插入排序或折半插入的依据,而希尔排序不会。
  2. 关于算法适用于顺序存储还是链式存储,主要看在算法的执行过程中,是否涉及到了对元素的随机访问,如果涉及到了对元素的随机访问,则链表不适用。就上述三种排序算法来说,直接插入排序 采用的是顺序查找,挨个找,链表也可以;而折半插入排序 采用的是折半查找,对mid指针来说,他是随机访问的,所以链表不行;希尔排序 也是同样的,因为每次增量d都是变化的,也是随机访问,所以链表不行。
    需要注意的是,大部分的排序算法都仅适用于顺序存储的线性表

发表评论

表情:
评论列表 (有 0 条评论,262人围观)

还没有评论,来说两句吧...

相关阅读

    相关 数据结构--插入排序

    算法中经常会用到各种各样的算法,比较简答的思想就是冒泡排序,一般刚开始编程时遇到排序问题时,会很容易想到冒泡排,冒泡排序是通过两辆比较数值,从而将数字移动到开始或者末尾的位置,

    相关 数据结构插入排序

    插入排序的基本思想是:每步将一个待排序的对象,按其关键字大小,插入到前面已经排好序的一组对象的适当位置上,直到对象全部插入为止。 简言之,边插入边排序,保证子序列中随时都是排

    相关 内部排序—直接插入排序

    直接插入排序是一种简单的排序方法,具体做法是:在插入第i个记录时,R1、R2…Ri-1已经排好序,这时候将Ri的关键字Ki依次与关键字Ki-1、Ki-2等进行比较,从而找到应该