https://blog.csdn.net/u013025748/article/details/116590656
由于归并排序的空间复杂度问题,快速排序比较适合来实现排序函数,但是,我们也知道,快速排序在最坏情况下也就是数组有序的时候的时间复杂度是 O(n2),如何来解决这个“复杂度恶化”的问题呢?
目录
一、如何优化快速排序?选择合适分区点:
O(n2) 时间复杂度出现的主要原因还是因为我们分区点选得不够合理。最理想的分区点是:被分区点分开的两个分区中,数据的数量差不多。最坏的情况就是每次都没有有效分区,导致所有数据都在一个区中。
因此想要优化快速排序算法,就需要选择合适的分区点:
1. 三数取中法
我们从区间的首、尾、中间,分别取出一个数,然后对比大小,取这 3 个数的中间值作为分区点。这样每间隔某个固定的长度,取数据出来比较,将中间值作为分区点的分区算法,肯定要比单纯取某一个数据更好。
但是,如果要排序的数组比较大,那“三数取中”可能就不够了,可能要“五数取中”或者“十数取中”。
2. 随机法
随机法就是每次从要排序的区间中,随机选择一个元素作为分区点。
这种方法并不能保证每次分区点都选的比较好,但是从概率的角度来看,也不大可能会出现每次分区点都选得很差的情况,所以平均情况下,这样选的分区点是比较好的。时间复杂度退化为最糟糕的 O(n2) 的情况,出现的可能性不大。
二、警惕堆栈溢出:
快速排序是用递归来实现的,递归要警惕堆栈溢出。为了避免快速排序里,递归过深而堆栈过小,导致堆栈溢出,有两种解决办法:
1、限制递归深度:
一旦递归过深,超过了我们事先设定的阈值,就停止递归。
2、在堆上模拟实现一个函数调用栈:
手动模拟递归压栈、出栈的过程,这样就没有了系统栈大小的限制。
三、示例:
比如如下示例,出现了栈溢出错误,原因是当原数列是有序时,递归次数太多。
现改用优化代码思路:
void qsort(int l, int r) {
int tmp = a[(l+r)/2], i = l, j = r;
while(i <= j) {
while(a[j]>tmp) j--;
while(a[i]<tmp) i++;
if(i<=j) {
int t = a[i]; a[i] = a[j]; a[j] = t;
i++;
j--;
}
}
if(i<r) qsort(i, r);
if(j>l) qsort(l, j);
}