快速排序
1.定义:
(1)快速排序是一种分治的排序算法,它将一个数组分成两个子数组,将两部分独立地排序。
(2)快速排序和归并排序时互补的:归并排序将数组分成两个子数组分别排序,并将有序的子数组归并以将整个数组排序;而快速排序将数组排序的方式则是当两个子数组都有序时整个数组也就自然有序了。
(3)归并排序时,递归调用发生在处理整个数组之前;快速排序时,递归调用发生在处理整个数组之后。
(4)归并排序中,一个数组被等分为两半;在快速排序中,切分的位置取决于数组的内容。
2.代码剖析
假设:数组a,最小下标low,最大下标high
(1)当我们将数组切分后,假设得到了切分的下标j,即对于某个j,a[j]已经排定;a[low]到a[j-1]中的所有元素都不大于a[j];a[j+1]到a[high]中的所有元素都不小于a[j]。
(2)因此,为了完成上述切分,一般策略是先随意地取a[low]作为切分元素;即那个将会被排定的元素;然后我们从数组的左端开始向右扫描直到找到一个大于等于它的元素,再从数组的右端开始向左扫描直到找得到一个小于等于它的元素。然后交换他们的位置。如此继续,我们就可以保证左指针i的左侧元素都不大于切分元素,右指针j的右侧元素都不小于切分元素。
(3)当两个指针相遇时,我们只需要将切分元素a[low]和左子数组最右侧的元素(a[j])交换然后返回j即可。如下图所示:
3.图解
4.算法代码
/**
* 快速排序
*
* @param a
*/
public static void quickSort(int[] a) {
quickSort(a, 0, a.length - 1);
}
private static void quickSort(int[] a, int low, int high) {
//当只有一个元素时,直接返回,无需排序
if (high <= low) {
return;
}
//获得切分的下标j
int j = partition(a, low, high);
//通过切分的下标j,分别对下标j左边数组以及右边数组进行快排,递归下去
quickSort(a, low, j - 1);
quickSort(a, j + 1, high);
}
private static int partition(int[] a, int low, int high) {
//将数组切分为a[low..i-1],a[i],a[i+1...high]
int i = low; //左右扫描指针3
int j = high + 1;
int v = a[low]; //切分元素
int e;
while (true) {
//扫描左右,检查是否结束并交换元素
while (a[++i] < v) {
if (i == high) break;
}
while (a[--j] > v) {
if (j == low) {
break;
}
}
//当两个指针相遇时,跳出循环
if (i >= j) {
break;
}
e = a[i];
a[i] = a[j];
a[j] = e;
}
//当指针相遇后,将切分元素和左子数组最右侧的元素交换然后返回j即可
e = a[low];
a[low] = a[j];
a[j] = e;
return j;
}
5.算法优劣分析
(1)优点:简洁性,比较次数少。
(2)排序效率最终还是依赖切分数组的效果,而这依赖于切分元素的值
(3)快速排序最好情况是每次都正好能将数组对半分
(4)存在缺点:在切分不平衡时这个算法可能会较为低效。例如:如果第一次从最小的元素切分,第二次从第二小的元素切分,如此这般,每次调用只会移除一个元素。这会导致一个大子数组需要切分很多次。因此,在我们对一个数组进行排序之前,我们可以先打乱这个数组的顺序来避免这种情况发生。
6.资料引用:
’《算法Algorithms(第4版)》 p182~p186
上一节:Java算法(4):归并排序