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;
//插入较小数
}
}
|