最近研究快排,对快排对低熵数据的效率很不满意,于是研究了对快排平均效率的提高的问题。
快排处理有序数据时,仍需要进行划分,一分到底,尤其是大部分数据集中在一个集合中,使快排退化为了冒泡。传统做法是在数据中任取一数作为划分轴,该法确实大大提高了快排的平均效率,但是这样仍不能达到对于已排序数据O(n)的时间效率。那么,还有什么办法改进呢?
我们发现,主要问题集中在传统快排无法对数据整体进行判断,所有情况都按照熵最高处理,那么,只要在快排中加入数据分析,就可以避免遇到低熵数据仍进行大量运算。
那么,我们在划分函数中,在high与low移动至中间时,比较相邻两个数据大小,并按照顺序进行交换,类似于冒泡法,并定义两个变量,对low与high是否交换过进行计数。若没有交换过,则表明该部分数据已经有序,在qsort函数递归时,不必再对low或high进行操作,即不必对low或high进行递归。这样,就大大减少了递归次数,降低了时间复杂度。如此一来,传统情况下的最差情况,似乎就成了最好情况,由O(n^2)变成了O(n)。
c版源代码:
void swap(int *px, int *py){
//定义交换函数
int temp;
temp = *px;
*px = *py;
*py = temp;
}
int partition(int data[], int low, int high, int *bol, int
*boh){
//定义划分函数
if(data[low] >= data[high]){
if (data[low] > data[(low + high) / 2]) swap(&data[low],
&data[(low + high) / 2]);
else if(data[high] > data[(low + high) / 2])
swap(&data[low], &data[high]);
}
else{
if(data[high] < data[(low + high) / 2]) swap(&data[low],
&data[high]);
else if(data[low] < data [(low + high) / 2])
swap(&data[low], &data[(low + high) /2]);
}//对data[low],data[high],data[(low + high) /
2]取中值与data[low]交换
//此处不推荐使用时间随机函数,因为好的随机函数本身有很大的时间复杂度
int key = data[low], countl = 0, counth = 0, n = high, m =
low;//countl,counth分别对low子表与high子表数据交换情况计数
int *temh = boh, *teml = bol;
while(low < high){
while(low < high && data[high] >= key){
if(high < n)
if(data[high] > data[high + 1]){
//进行冒泡操作
swap(&data[high], &data[high +
1]);
//对交换操作进行计数
counth = 1;
}
--high;//移动指针
}
data[low] = data[high];//交换高低指针数据
while(low < high && data[low] <= key){
if(low > m)
if(data[low - 1] > data[low]){
//进行冒泡操作
swap(&data[low - 1], &data[low]);
//对交换操作进行计数
countl = 1;
}
++low;//移动指针
}
data[high] = data[low];//交换高低指针数据
}
data[low] = key;
*temh = counth;
*teml = countl;
return low;
}
void qsort(int data[], int low, int high){
//定义递归实现qsort函数
if(low == high) return;//递归终点
else{
int bol,boh;
int key = partition(data, low, high, &bol, &boh);
if(bol ^ 0) qsort(data, low, key -
1);//根据标记判断是否对low子表进行操作
if(boh ^ 0) qsort(data, key + 1,
high);//根据标记判断是否对high子表进行操作
return;
}
}
好不容易调出来了,因为疏忽,把l和h搞反了,调了两晚上才弄出来,无语……再也不犯低级错误了!·¥#%!
测试者:stevenjl
一、测试系统
CPU: Intel Celeron 4 2.0 GHz Memory: 256MB
System: Windows XP SP2 \ Cena 0.6
二、待测试算法(均用C语言编写)
1.快速排序(无优化,追求程序简单)【Qsort】
2.随机化快速排序【Rnd_Qsort】
3.楼主提供的程序【DZ_Qsort】
4.希尔排序【Shell】
三、数据
测试点
长度
备注
1
1024
随机数据(随机范围0~maxint,下同)
2
10240
随机数据
3
102400
随机数据
4
102400
随机数据
5
1024000
随机数据
6
2000000
递增数据(1,2,3…2000000)
7
2000000
递减数据(2000000…3,2,1)
8
2000000
峰状数据(1,2,3…1000000…3,2,1)
9
2000000
相同数据(32767…)
10
2000000
谷状数据(1000000…3,2,1,1,2,3…1000000)
四、文件长度(已经删除所有注释和段首空格)
程序
长度
Qsort
286字符
Rnd_Qsort
359字符
DZ_Qsort
1184字符
Shell
200字符
五、测试成绩
测试点
Qsort
Rnd_Sort
DZ_Qsort
Shell
1
0.01
0.01
0.01
0.03
2
0.03
0.03
0.01
0.03
3
0.26
0.25
0.23
0.29
4
0.25
0.25
0.26
0.26
5
2.39
2.32
2.39
3.31
6
60.06(超时中止)
5.09
4.85
5.2
7
60.08(超时中止)
5.17
4.9
5.25
8
60.13(超时中止)
5.07
4.84
5.18
9
60.05(超时中止)
4.56
4.29
4.6
10
60.03(超时中止)
5.06
39.03s后崩溃
4.85
注:测试方法与数据参考Wasltone在OIBH的相关帖子
好久之前贴在oifans上了,其实觉得还有改进的余地。懒得弄了,谁喜欢谁去尝试一下吧。应付oi这个足够了。