折半插入排序
-
直接插入排序
算法简单,但仅适合于待排序记录数量小的情况
;
当待排序记录的数量
很大时,需要对直接插入排序方法
做出改进。出发点是减少比较
和移动
两个操作的次数。
-
因为查找
下一条待排序记录的插入位置
是在有序序列区
进行查找的,则可以使用折半查找
来定位。如此实现的插入排序为折半插入排序
。 -
因此
折半排序的步骤
如下:
1、折半查找,在有序序列区
中找到最后一个关键字不小于 R[i].key 的记录
2、每一趟
插入一条记录
到有序序列区
,i = 2, 3, …, n。实现整个序列
的排序。 -
重排记录的代码如下:(之前只是修改
指针
,现在修改记录地址
)
void BiInsertionSort(SqList &L)
{
for(i=2; i<=L.length; i++)
{
L.r[0] = r[i]; // 暂存,哨兵
// 折半查找寻找插入位置
low = 1;
high = i-1;
while(low <= high)
{
m = (low+high) / 2;
if(L.r[0].key < L.r[m].key)
{
high = m-1; //插入点在低半区
}
else
{
low = m+1; // 插入点在高半区
}
}
//已找到插入位置 (high + 1),开始后移
for(j=i-1; j>=high+1; j--)
{
L.r[j+1] = L.r[j];
}
L.r[high+1] = L.r[0];
}
}
- 上述算法其实就是两步,
比较
和插入
。(比较
是用来找到插入
的位置)
减少了比较次数
,比较次数
是 O(nlogn).但没改变插入次数
,因此总的时间复杂度
仍然是 O(n2)。 - 为了减少
插入次数
,引入下面的表插入排序
。
表插入排序
-
为了减少
在排序过程中进行的移动记录的操作次数
,必须改变排序过程中采用的存储结构
(静态链表):
1、利用静态链表
进行排序,排序过程中只改变指针
,不改变记录位置
;
2、排序完成之后,一次性地调整各个记录相互之间的位置,即将每个记录都调整到它们所应该在的位置上。 -
下面的算法是第二步的实现
#define SIZE 100 // 静态链表容量
typedef struct
{
RcdType rc; // 记录项
int next; // 指针项
} SLNode;
typedef struct
{
SLNode r[SIZE]; // 0号单元作为表头结点
int length; // 链表的当前长度
} SLinkListType; // 静态链表类型
void Arrange(SLinkListType &SL)
{
//根据静态链表SL中各结点的指针值调整记录位置,使得SL中记录按关键字
//非递减有序顺序排列
p = SL.r[0].next; // p 指示第一个记录的当前位置
for(i=1; i<SL.length; i++)
{
while(p < i)
{
// 此时数组中所有小于i的分量都已经“到位”了
p = SL.r[p].next;
}
q = SL.r[p].next;
if(q != i)
{
SL.r[p] <-- --> SL.r[i]; //连个记录交换,赋值次数是3,减少了记录移动的次数
SL.r[i].next = p;
}
p = q;
}
}
移动记录的次数
减少了,最坏的情况是n-1
次交换,也就是3 * ( n-1 )
次记录移动。
比较记录的次数
没变,
所以时间复杂度
仍然是 O( n2 )