@quinn
2015-03-18T17:56:30.000000Z
字数 1769
阅读 2862
排序算法
由于快速排序会递归的调用自身的许多小文件,因而要对小的子文件尽可能使用高效的算法
直观来想即,if(r-l <= M) insertion(a, l, r);
,M为子文件规模的阈值。
而一种更高效的实现方式是,在快速排序过程中直接忽略小的子文件,
if(r-l <= M) return;
在快速排序完成后,再进行一次整个数组的插入排序。即使子文件的阈值设置较大,因为只有极少数的文件进行了划分操作,所以快速排序部分运行很快;而插入排序操作的对象是几乎有序的文件,所以插入排序部分运行也很快。这个忽略小文件技术无论何时都适用于递归算法,因为本质在于,我们确信所有递归算法在执行小问题实例时会占据时间的大部分。因而可以采用一些混合算法改进总的运行时间。
具体程序上的改进,参见三者取其中的程序实现
对快速排序的另一项改进,即使用一个尽可能在文件中间划分的划分元素(避免最坏情况):对此有2种办法,一种是采用数组中的一个随机元素,但是在实际应用中不可能使用随机数生成器;
而简单的随意选择可能也是高效的,因此可得到我们另一种划分元素的方法:
从文件中取出三个元素,使用三个元素的中间元素作为划分元素。取数组的最左右两边的元素a[l] a[r]和中间位置的元素a[(l+r)/2],对这三个数排序,用a[r-1]和a[(l+r)/2]交换,然后对(l+1, r-1) 的数组进行划分,这一改进成为三者取中法 。
/*=================三者取其中 快速排序 (需要最后使用插入排序)=======================*/
void quick_sort_middle(Item a[], int l, int r)
{
if(r-l <= M) //小于M的文件不排序,最后统一用插入排序
return;
exch(&a[r-1], &a[(l+r)/2]);
//三交换法对这3个元素排序
comp_exch(&a[l], &a[r-1]);
comp_exch(&a[l], &a[r]);
comp_exch(&a[r-1], &a[r]);
int i = partation(a, l+1, r-1);//a[l]<a[r-1], a[r]>a[r-1]
quick_sort_middle(a, l, i-1);
quick_sort_middle(a, i+1, r);
}
带有大量重复关键字值使用基本的快速排序性能会极其低效。
直观想是将文件分为3部分:
三路划分法:将标准的划分作如下改动,扫描时将遇到左子文件中和划分元素相等的元素放在文件的最左边,遇到右子文件中和划分元素相等的元素放在文件的最右边。最后在将左右相等的关键字交换到中间,此时,这些元素都已经在最终位置,从递归调用中去除它们。
/*==========三路法快速排序--可适用于重复关键字(需要最后使用插入排序)=========*/
void quick_sort_3_way(Item a[], int l, int r)//划分操作已经包含在此函数中
{
if(r-l <= M)
return;
int i, j, p, q;
i = l-1, j = r;
p = l, q = r;
Item v = a[r];
for(;;)
{
while(less(a[++i], v));
while(less(v, a[--j]))
if(j == l)
break;
if(j <= i)
break;
exch(&a[i], &a[j]);
//交换和划分元素相等的元素到左右两边
if(a[i] == v)
exch(&a[i], &a[p++]);
if(a[j] == v)
exch(&a[j], &a[q--]);
}
exch(&a[i], &a[r]);
//将相等元素交换到文件中间
i = i-1, j = i+1;
for(int k = l; k < p; k++, i--)
exch(&a[k], &a[i]);
for(int k = r; k > q; k--, j++)
exch(&a[k], &a[j]);
//递归处理子文件
quick_sort_3_way(a, l, i);//此时i所处的位置如图1所示
quick_sort_3_way(a, j, r);
}
http://pan.baidu.com/s/1o6l2qN4
参考资料:《算法:C语言实现》P201