快速排序:会先把数组中的一个数当做基准数,一般会把数组中最左边的数当做基准数。 然后从两边进行检索。先从右边检索比基准数小的。再从左边检索比基准数大的。如果检索到了,就停下,然后交换这两个元素,再继续检索。当两个左边与右边检索的游标相遇时,将这个值与基准数交换,一轮排序结束,准备进行下次的递归排序。
原理图解:
动态图解:
代码实现:
package blog;
import java.util.Random;
/**
* 快速排序
*/
public class QuickSort {
public static void main(String[] args) {
// int arr[] = { 2, 3, 23, 41, 4, -7, 26 };
// quickSort(arr, 0, arr.length - 1);
// for (int i = 0; i < arr.length; i++) {
// System.out.print(arr[i] + " ");
// }
testQuickSort();
}
public static void quickSort(int[] arr, int left, int right) {
// 递归先写递归出口
if (left > right)
return;
// 将最左边作为基准数
int base = arr[left];
// 左边从下标i开始
int i = left;
// 右边从下标j开始
int j = right;
// 只有当两边的下标不相等时才进行检索
while (i != j) {
// j是从右往左找比基准数小的就停下,换句话说,如果找到比基准数大或者的与基准数相等的就继续
while (arr[j] >= base && i < j) {
// 没有找到就继续
j--;
}
// i是从左往右找比基准数小的就停下,换句话说,如果找到比基准数大或者的与基准数相等的就继续
while (arr[i] <= base && i < j) {
// 没有找到就继续
i++;
}
// 当不满足这两个while条件时就代表j找到了比基准数小的,i找到了比基准数大的,此时可以进行交换
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
// 将相遇的数与基准数交换,结束一轮排序,这里写成arr[j]也可以
arr[left] = arr[i];
arr[i] = base;
// 递归,在数组arr中从下标left~i-1再进行排序
quickSort(arr, left, i - 1);
// 递归,在数组arr中从下标j+1~right再进行排序
quickSort(arr, j + 1, right);
}
// 定义一个测试类,来测试快速排序的效率
public static void testQuickSort() {
// 定义一个任意大的数组
int[] arr = new int[1000000];
// 生成对应的随机数将其存入数组中
Random r = new Random();
for (int i = 0; i < arr.length; i++) {
arr[i] = r.nextInt();
}
// 计算当前系统时间,以毫秒表示
long start = System.currentTimeMillis();
quickSort(arr, 0, arr.length - 1);
// 计算当前排序结束时间
long end = System.currentTimeMillis();
// 快速排序所花的时间
System.out.println(end - start);
}
}
时间复杂度分析:
最好情况:
在最好的情况下,每次我们进行一次分区,我们会把一个序列刚好分为几近相等的两个子序列,这个情况也我们每次递归调用的是时候也就刚好处理一半大小的子序列。这看起来其实就是一个完全二叉树,树的深度为 O(logn),所以我们需要做 O(logn) 次嵌套调用。但是在同一层次结构的两个程序调用中,不会处理为原来数列的相同部分。因此,程序调用的每一层次结构总共全部需要 O(n) 的时间。所以这个算法在最好情况下的时间复杂度为 O(nlogn)。
事实上,我们并不需要如此精确的分区:即使我们每个基准值把元素分开为 99% 在一边和 1% 在另一边。调用的深度仍然限制在 100logn,所以全部运行时间依然是 O(nlogn)。
最坏情况:
事实上,我们总不能保证上面的理想情况。试想一下,假设每次分区后都出现子序列的长度一个为 1 一个为 n-1,那真是糟糕透顶。这一定会导致我们的表达式变成:
T(n) = O(n) + T(1) + T(n-1) = O(n) + T(n-1)
这和插入排序和选择排序的关系式真是如出一辙,所以我们的最坏情况是 O(n²)。
经测试,在对十万个随机数进行排序的时候快速排序所花的时间为2毫秒左右,在对一百万个随机数进行排序的时候快速排序所花的时间大约为15毫秒左右,而冒泡排序大约需要24秒,在对一千万个数进行排序的时候快速排序所花的时间大约为1.5秒,而冒泡排序没有几个小时很显然是完不成的。由此可见快速排序的效率是很高的。