排序算法(六)二分双插入排序

1、为减少二分插入排序中的比较及移动次数,可考虑一次以两个数据为单位进行插入。以升序为例,每次插入时先找出两个待插入数据中的较大者,按二分查找法确定其位置,在向后移动已有序记录时一次移动两个位置(因为较小记录肯定将来放在较大记录之前);插入较大数据后再按传统二分插入排序算法在较大数据所处位置与第一条数据所处位置之间插入较小数据。


2、因为在插入较大数据时已经缩小了较小数据的查找范围,同时,在给较大记录移出存放空间时已经提前将一部分本来应该在插入较小数据时才移动的记录提前一趟进行了移动,从而既减少了确定较小记录位置时的比较次数,又减少了移动次数,性能得到了改善。


3、算法描述如下(记录即数据):

假定R[1]到R[i-1]间的记录已经是有序的,现要将从R[i]到最后的所有记录再插入到有序序列中去,可按以下步骤进行:
(1)将 r[i]和 r[i+1]中较大记录放入 max,较小记录放入 min;

(2)用二分查找法确定 max 在 r[1]到 r[i-1]这一段中应该所处的位置,用 high1 表示;

(3)将从 r[high1+1]到 r[i-1]这段中的记录向后移动两条记录的位置;

(4)将 max 放入 r[high1+2];

(5)用二分查找法确定 min 在 r[1]到 r[high1]这一段中应该所处的位置,用 high2 表示;

(6)将从 r[high2+1]到 r[high1]这段中的记录向后移动一条记录的位置;

(7)将 min 放入 r[high2+1];

(8)重复(1)(8)的过程,直到所有记录插入完为至。


4、本文先介绍一些定义,前面的文章有点乱,而且说明起来比较费力,还是决定按数据结构的方法来贴代码。

下面是一些定义:

#include <iostream>
#include <ctime>
using  namespace  std;

//#define MAXSIZE 50
const  unsigned  int  MAXSIZE = 50;   // 最大长度
 
typedef  int  KeyType;      // 关键字类型,假定为int
typedef  double  InfoType;  // 其它部分数据类型,此处假定为double
 
typedef  struct
{
     KeyType key;       // 关键字项
     InfoType otherinfo;  // 其它数据项
}RedType;   // 记录类型
 
 
typedef  struct
{
     RedType R[MAXSIZE + 1];  // 0号元素不存放有效数据,R[0]闲置或用作哨兵单元
     int  Length;                // 顺序表长度
}SqList;    // 顺序表类型


另外,附上两个函数,作用分别是用随机数初始化顺序表 和 显示当前顺序:

void  RandomInit(SqList* L,  int  nMax  /*= NULL*/ )
{
     L->Length = MAXSIZE; //设置总个数
     
     srand ( (unsigned)  time (NULL) ); //初始化随机函数
     
     for ( int  i = 1; i <= L->Length; i++) //生成原始无序数据
     {
         if  (nMax != NULL)
         {
             L->R[i].key =  rand () % nMax;
         }
         else
         {
             L->R[i].key =  rand ();
         }
         
         L->R[i].otherinfo =  rand ();
     }
}

void  PrintNowOrder(SqList* L)
{
     for  ( int  i = 1; i <= L->Length; i++)
     {
         cout << L->R[i].key <<  "\t" ;
     }
     cout << endl;
}

5、事实上,我需要多次使用上述内容,所以将上述内容封装成一个类了,此处就不提了,下面是二分双插入排序的实现代码:

// 二分双插入排序算法
void  BinaryDoubleInsertSort(SqList *L)
{
     int  i, j, nHigh1, nLow1, nHigh2, nLow2, nMid, nBegin;
     RedType nMax, nMin;
 
     // 确定插入的起始位置,偶数个则从第一个开始插入,奇数个则从第二个开始插入
     nBegin = (L->Length % 2 == 0) ? 1 : 2;
 
     for (i = nBegin; i + 1 <= L->Length; i += 2)
     {
         // 将待插入两条记录按大小分别放入 nMax,nMin
         if (L->R[i].key <= L->R[i+1].key) 
         {
             nMin = L->R[i];
             nMax = L->R[i+1];
         }
         else
         {
             nMin=L->R[i+1];
             nMax=L->R[i];
         }
 
         nLow1 = 1;
         nHigh1 = i - 1; 
 
         // 用二分法确定较大数的位置
         while (nLow1 <= nHigh1)
         {
             nMid = (nLow1 + nHigh1) / 2;
             if (nMax.key < L->R[nMid].key)
             {
                 nHigh1 = nMid - 1;
             }
             else
             {
                 nLow1 = nMid + 1;
             }
         }
 
         // 后移记录,为待插入数提供位置,一次移两个
         for (j = i - 1; j >= nHigh1 + 1; j--)
         {
             L->R[j + 2] = L->R[j];
         }
 
         L->R[nHigh1 + 2] = nMax; // 插入较大数
         nLow2 = 1;
         nHigh2 = nHigh1;  // 用二分法确定较小数插入位置,只在较大数位置及起始位置之间查询即可
 
         while (nLow2 <= nHigh2)
         {
             nMid = (nLow2 + nHigh2) / 2;
 
             if (nMin.key < L->R[nMid].key)
             {
                 nHigh2 = nMid - 1;
             }
             else
             {
                 nLow2 = nMid + 1;
             }
         }
 
         for (j = nHigh1;j >= nHigh2 + 1; j-- ) //后移记录,为待插入数提供位置,一次移一个
         {
             L->R[j + 1] = L->R[j];
         }
 
         L->R[nHigh2 + 1] = nMin;  //插入较小数
     }
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值