详谈内部排序之各种插入排序

插入排序:

直接插入排序:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5xIovOHw-1593738255371)(C:\Users\唐志瑞\AppData\Roaming\Typora\typora-user-images\1593733886107.png)]

如上图的插入扑克牌就是生活中最常见的插入排序。

直接插入排列过程:

先将序列中第 1 个记录看成是一个有序子序列,

然后从第 2 个记录开始,逐个进行插入,直至整个序列有序。

例题:
在这里插入图片描述

为直接插入排序的详细过程,其中有一些注意事项:

1)数据中有两个49,其中一个加粗,用来判断这两个49的前后顺序是否发生变化,然后得到该排序是否稳定

2)从初始状态开始只要i++,就会向后读一个数据,与前面的数据进行循环比较,然后插入到正确的位置,直到整个序列有序。

3)R0的作用是:存储需要进行插入操作的数据,称为监视号。

关键代码:

void InsertSort ( SqList &L ) { 
  // 对顺序表 L 作直接插入排序
     for ( i = 2; i <= L.length; ++ i )  
       if (L.r[i].key < L.r[i -1].key) { 
			L.r[0] = L.r[i];            // 复制为监视哨 
			L.r[i] = L.r[i -1]; 
			for ( j = i - 2; L.r[0].key < L.r[ j ].key;  - - j )  
    			L.r[ j + 1] = L.r[ j ];  // 记录后移
			L.r[ j + 1] = L.r[0];        // 插入到正确位置 
       } 
} // InsertSort 

直接插入排序的性能分析:

在这里插入图片描述

综上,由于待排记录序列是随机的,取上述二值的平均值。所以直接插入排序的时间复杂度为O(n^2)。

同时,直接插入排序是“稳定的”:关键码相同的两个记录,在整个排序过程中,不会通过比较而相互交换。


折半插入排序:

​ 如上为直接插入排序的大致内容,但是对于这个操作,我们可以考虑到 L.r[1,…,i-1] 是按关键字有序的有序序列,则可以利用折半查找实现“L.r[1,…,i-1]中查找 L.r[i] 的插入位置” 。

所以对于如此实现的插入排序,我们称之为为折半插入排序。

折半插入排序过程:

​ 折半插入排序在寻找插入位置时,不是逐个比较而是利用折半查找的原理寻找插入位置。待排序元素越多,改进效果越明显。

**例题:**有6个数据记录,前5个已排序的基础上,对第6个记录排序。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-me70okNY-1593738222822)(C:\Users\唐志瑞\AppData\Roaming\Typora\typora-user-images\1593735853877.png)]

如图前五个是已经排列好的5个数据。

1)将已排列好的5个数据中 low = 15 , mid = 36, high = 69

2)判断 需要排列的数42mid 的关系,例题中是大于关系 ,所以改变low和mid的值即:low = 53,mid =53

3)再次判断,得到小于关系,所以改变low和high的值: high = 36 , low = 53

4)得到high<low,结束查找,得到42的插入位置为low或者high+1

注意:

1)mid,low,high ,这三个变量记录的是数据在数组中的下标,不是数据本身的值。

2)在进行折半的时候,大于关系,就将low的值变成mid+1,即:low = mid+1

​ 小于关系,就将high的值变成mid-1,即:high = mid-1

关键代码:

void BinsertSort ( SqList &L){  // 折半插入排序

  int i,low,high,mid;

  for(i=2; i<= L.length; ++i) {

   L.r[0]=L.r[i];         //将L.r [i] 暂存到L.r[0]

   low=1; high=i-1;

    While(low<=high) {   //比较,折半查找插入位置

      mid=(low+high)/2;  // 折半

      if (L.r[0].key< L.r[mid].key) high=mid-1;  //插入点在低半区

      else low=mid+1; }  // 插入点在高半区

    for( j=i-1; j>=low; - -j ) L.r[j+1]=L.r[j];  // 记录后移

    L.r[low]=L.r[0]; }  // 插入

}// BInsertSort

折半插入排序的性能分析:

在这里插入图片描述


     对于插入排序还有:2-路插入排序,表插入排序等。
     下面只给出这两个排序的基本思路,大家感兴趣的话可以自己进行尝试实现

2-路插入排序

(1) 基本思想:

​ 2-路插入排序是在折半插入排序的基础上改进的,目的是减少排序过程中移动记录的次数,但为此需要n个记录的辅助空间。

(2) 具体做法:

​ 另设一个和 L.r 同类型的数组d,首先将 L.r[1] 赋值给d[1] ,并将d[1]看成是在排好序的序列中处于中间位置的记录,然后从 L.r中第 2 个记录起依次插入到d[1]之前或之后的有序序列中。先将待插入记录的关键字和d[1] 的关键字进行比较。

若 L.r[i]<d[1].key,则将 L.r[i] 插入到d[1] 之前的有序表中。反之,插入到d[1] 之后的有序表中。

折半插入排序的性能分析:

​ 2-路插入排序只能减少移动记录的次数,而不能绝对避免移动记录。 2-路插入排序中,移动记录的次数约为n^2/8。

​ 当L.r[1]是待排序记录中关键字最小或最大的记录时,2-路插入排序就完全失去了它的优越性。


表插入排序:

(1) 基本思想

​ 通过改变排序过程中采用的存储结构,减少在排序过程中进行“移动”记录的操作。利用静态链表进行排序,并在排序完成之后,一次性地调整各个记录相互之间的位置,即将每个记录都调整到它们所应该在的位置上。

(2) 具体做法

​ 首先将静态链表中数组下标为“1”的分量(结点)和表头结点构成一个循环链表,然后依次将下标为“2”至“n”的分量(结点)按记录关键字非递减有序插入到循环链表中。

表插入排序性能分析 :

​ 从表插入排序的过程可见,表插入排序的基本操作仍是将一个记录插入到已排好序的有序表当中。和直接插排序相比,不同之处仅是以修改2n次指针值代替移动记录,排序过程中所需进行的关键字间的比较次数相同。因此表插入排序的时间复杂度仍是O(n2)。

​ 表插入排序的结果只是求得一个有序链表,则只能对它进行顺序查找,不能进行随机查找,为了能实现有序表的折半查找,尚需对记录进行重新排列。

下期前言:

1.我们都能理解,优秀排序算法的首要条件就是

2.于是人们想了许许多多的排序办法,目的就是为了提高排序的速度。

3.而在很长的时间里,众人发现尽管各种排序算法花样繁多,但时间复杂度都是O(n^2),似乎没法超越了。

4.计算机学术界充斥着“排序算法不可能突破O(n^2)”的声音?

​ 终于有一天,当一位科学家发布超越了O(n^2) 新排序算法后,紧接着就出现了好几种可以超越O(n^2) 的排序算法,并把内排序算法的时间复杂度提升到了O(nlog2n)。“不可能超越O(n^2) ”彻底成为了历史。

之后我们会详细讲解,比插入排序更快的希尔排序


总结:

​ 大家需要自己动手实现这些排序(尤其是前两个排序方法),可以更加熟练的使用这些方法。

​ 对于上面有问题的地方大家可以提出来,我们可以一起讨论,我会积极改正。

​ 希望本片文章对你有用,谢谢!

  • 9
    点赞
  • 0
    评论
  • 1
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 深蓝海洋 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值