快速排序详解与java程序模版
1. 快速排序
1.1 快速排序的思想
快速排序本质上是荷兰国旗问题的拓展(建议先了解一下荷兰国旗问题,再食用更佳),算法思路如下:
-
定义数组
array的待排序范围为lo到hi,默认就是0到length-1,即整个数组都排序 -
将排序数组的最后一个数字
array[hi]作为基准数字baseNum,执行下面的partition过程partition的目标就是基于基准数字,将小于baseNum的放在数组左边,等于的放中间,大于的放右边,并且返回小于区域和大于区域的位置- 在数组的左边界前维护一个小于区域指针,这个指针之前的都是小于基准数字的数,指针的初始值为
lo-1 - 同样数组的右边界后维护一个大于区域指针,这个指针之后的都是大于基准数字的数,指针的初始值为
hi(这里因为将最后一个array[hi]作为基准了,因此待比较的数在hi前一个开始) - 定义一个指针
index,从lo出发,如果array[index] < array[hi],此时将小于区域的后一个数字和当前index的值交换,并让index++,小于区域也向右扩展一个 - 如果
array[index] = array[hi],表示当前的区域为等于基准数字的,因此只需要将index++即可 - 如果
array[index] > array[hi],表示当前值应当是大于区域的值,所以将其与大于区域的前一个交换,然后将大于区域左移,(注意这里不能将index++,因为刚刚换过来的数不知道其大小如何,需要再比较一次) - 重复执行上述过程,当
index和大于区域相遇停止,返回小于区域和大于区域的边界
-
将得到的新的小于区域和大于区域进行
partition过程,中间等于区域已经是最终的位置
注意点:
- 实际中的排序不是直接取数组的最后一个,而是随机的选择中间一个,然后让其和最后一个数交换,这样可以保证时间复杂度位\(O(NlogN)\),否则在顺序的数组这种最坏情况下时间复杂度位\(O(N^2)\),概率论证明。
1.2 java代码:
package com.company.com.chao.blog.sort;
import java.util.Arrays;
import java.util.Random;
public class QuickSort {
public static void main(String[] args) {
int[] array = new int[100];
Random r = new Random();
for (int i = 0; i < array.length; i++) {
array[i] = r.nextInt(100);
}
System.out.println("Before QuickSort: " + Arrays.toString(array));
quickSort(array, 0, array.length - 1);
System.out.println("After QuickSort: " + Arrays.toString(array));
}
/**
* 核心实现就是荷兰国旗问题:每次选择最右边的点作为边界,
* 然后将小于该点的数放到左边,等于的放中间,大于的放右边,
* 对左右边界继续执行上述过程
* 注意点:1. 每次随机从数组选择一个点,将其与右端点交换作为基准节点,
* 这样可以将复杂度从O(N^2)降低到O(NlogN),概率论证明
*/
private static void quickSort(int[] array, int lo, int hi) {
if (lo >= hi) return;
// 随机的从lo到hi上选择一个数字,这里操作的是索引
int base = (new Random().nextInt(hi - lo + 1)) + lo;
// 将这个数字和最后的位置hi交换
swap(array, base, hi);
// 执行一次partition过程
int[] nextPosition = partition(array, lo, hi);
// 将一次partition过程的左右边界进行下一次的递归排序
quickSort(array, lo, nextPosition[0] - 1);
quickSort(array, nextPosition[1] + 1, hi);
}
// 将hi的位置作为基准 将array的数据划分为三部分 前面的小于array[hi]
// 中间的等于array[hi], 后面的等于array[hi]
private static int[] partition(int[] array, int lo, int hi) {
// 定义两个指针
int leftIndex = lo - 1, rightIndex = hi, index = lo;
while (index < rightIndex) {
if (array[index] < array[hi]) {
// 让i处的和L区的右边一个交换
swap(array, ++leftIndex, index++);
} else if (array[index] == array[hi]){
index++;
} else {
// 让i处的和R区的左边交换
swap(array, --rightIndex, index);
}
}
// 交换最后的hi所在的基准数和大于区域的左边界
swap(array, rightIndex, hi);
return new int[]{leftIndex + 1, rightIndex};
}
// 将数组中的两个索引对应的位置的值交换
private static void swap(int[] array, int i, int j) {
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}

浙公网安备 33010602011771号