目录
1、描述
快速排序也是基于递归实现的。和归并排序不同的是:归并排序将数组不断的二等分,而快速排序是根据子数组的第一个元素的大小将数组分成两部分的。
第一步:根据数组的第一个元素a[0]=key的大小,将比key小的子数组a中,将比key大的放在子数组b中。
第二步:将子数组a 、子数组b和key合并成一个数组,key放在了中间位置。
第三步:继续将子数组a继续进行第一二步,b也一样。
..................不断递归,这个数组就变得有序了。
{注意:这里的子数组a,b只是抽象的概念,实际的算法为了节省空间消耗,是在原数组身上来对数组进行拆分的。}
2、特点
- 时间复杂度O(N*lgN)
- 原地排序(不需要太多额外空间,只需要一个很小的辅助栈)
- 快速排序的内循环比大多数排序算法都要短小,所以它在理论和实际上都要更快。
- 主要缺点:非常脆弱,要非常小心才能避免低劣的性能。
3、代码实现
public class Quick {
public static void sort(double[] a) {
sort(a, 0, a.length - 1);
}
public static void sort(double[] a, int lo, int hi) {
if (lo >= hi)
return;
int mid = partition(a, lo, hi);
sort(a, lo, mid - 1);
sort(a, mid + 1, hi);
}
private static int partition(double[] a, int lo, int hi) {
double key = a[lo];
int left = lo;
int right = hi + 1;
while (true) {
while (key > a[++left]) if (left == hi) break;
while (key < a[--right]) if (right == lo) break;
if (left >= right) break;
exch(a, left, right);
}
exch(a, lo, right);
return right;
}
private static void exch(double[] a, int i, int j) {
double temp = a[i];
a[i] = a[j];
a[j] = temp;
}
private static void show(double[] a) {
System.out.println("\n");
for (double item : a) {
System.out.print((int) item + ",");
}
}
public static void main(String[] args) {
double[] a = { 55, 43, 23, 12, 13, 11, 7, 8, 88, 6, 3, 2, 4, 1, 9, 8, 7, 11, 56, 45, 22, 23,
45, 66 };
sort(a);
show(a);
}
}
3.1、切分函数partition()图示
4、性能
优势:快速排序切分方法的内循环会用一个递增的索引将数组元素和一个定值比较。这种简洁性 也是快速排序的一个优点,很难想象排序算法中还能有比这更短小的内循环了。例如,归并 排序和希尔排序一般都比快速排序慢,其原因就是它们还在内循环中移动数据。
风险:在切分不平衡时这个程序可能会 极为低效。例如,如果第一次从最小的元素切分,第二次从第二小的元素切分,如此这般,每次调用 只会移除一个元素。这会导致一个大子数组需要切分很多次。我们要在快速排序前将数组随机排序的 主要原因就是要避免这种情况。它能够使产生糟糕的切分的可能性降到极低,我们就无需为此担心了。
总结:总的来说,可以肯定的是对于大小为 N 的数组,快速排序 的运行时间在 1.39NlgN 的某个常 数因子的范围之内。归并排序也能做到这一点,但是快速排序一般会更快(尽管它的比较次数多 39%),因为它移动数据的次数更少。
5、快速排序优化(小数组插入排序)
当我们将大的数组递归拆分成小的数组的,数组长度低于7的时候,我们使用插入排序,而不是让它继续递归下去,这样可以加快排序的速度。
6、快排优化(三向切分)
三向切分是为了解决数组中存在大量重复数据进行的一种优化,原版快速排序是将数组分成 大于key,和小于key两部分,现在将数组分成大于,小于,和等于三部分。
对于存在大量重复元素的数组,这种方式的排序要比标准的快速排序效率高很多。
public static void sort(double[] a) {
sort(a, 0, a.length - 1);
}
public static void sort(double[] a, int lo, int hi) {
if (lo >= hi)
return;
double key = a[lo];
int left = lo;
int right = hi;
int i=lo+1;
while (i<=right) {
if (a[i]<key)exch(a,i++,left++); //将比key小的放在子数组的前面
else if (a[i]>key)exch(a,i,right--);//将比key大的放在子数组的后面
else i++;//如果等于,就跳过继续下一个
}
sort(a, lo, left - 1);// {3,2,1,4,4,4,4,9,8,7}继续递归{3,2,1}的部分
sort(a, right + 1, hi);// {3,2,1,4,4,4,4,9,8,7}继续递归{9,8,7}的部分
}
private static void exch(double[] a, int i, int j) {
double temp = a[i];
a[i] = a[j];
a[j] = temp;
}