写在前面:
快速排序是冒泡排序的升级,他的排序思想为:对于给定的一组记录,选择一个基准元素,通常选择第一个元素或者最后一个元素,通过一趟扫描,将待排序列分成两部分,一部分比基准元素小,一部分大于等于基准元素,此时基准元素在其排好序后的正确位置,然后再用同样的方法递归地排序划分的两部分,直到序列中的所有记录均有序为止。
例如:数组int [] arr = {4,3,5,6,1,2},选取基准点为第一个元素4,基准点与最右边j=5开始比较
- 第一次比较,与右边比较:4>2,arr[i++]=2 【 2,3,5,6,1,2】
- 第二次比较,与左边比较:4>3,i++ 【 2,3,5,6,1,2】
- 第三次比较,与左边比较:4<5,arr[j--]=5 【 2,3,5,6,1,5】
- 由于i<j,所以继续循环
- 第四次比较,与右边比较:4>1,arr[i++]=1 【 2,3,1,6,1,5】
- 第五次比较,与左边比较:4<6,arr[j--]=6 【 2,3,1,6,6,5】
- i=j=3,所以结束第一轮排序。令arr[i]=index 【 2,3,1,4,6,5】
这样我们就完成了第一轮排序,接下里会继续递归,原数组以基准点4为界限。分成了2个数组:
左边数组-----2,3 ,1 右边数组-----6,5 这样左边的元素都<基准点4,右边的都>基准点4。然后,对两边的数组继续快速排序,直到序列中的所有记录均有序为止。
代码实现:
- package test3;
- import java.util.Arrays;
- public class Test {
- public static void main(String[] args) {
- int[] arr = { 4, 3, 5, 6, 1, 2 };
- quicksort(arr);
- System.out.println(Arrays.toString(arr));
- }
- private static void quicksort(int[] arr) {
- sort(arr, 0, arr.length - 1);
- }
- private static void sort(int[] arr, int low, int height) {
- int i;// 记录左边的指针
- int j;// 记录右边的指针
- int index;// 记录基准点的值
- if (low > height) {
- return;
- }
- i = low;
- j = height;
- index = arr[i];// 基准点设置为左边第一个元素
- while (i < j) {
- while (i < j && index < arr[j]) {
- j--;
- }
- if (i < j) {
- arr[i++] = arr[j];
- }
- while (i < j && index > arr[i]) {
- i++;
- }
- if (i < j) {
- arr[j--] = arr[i];
- }
- }
- // 第一轮分组结束
- arr[i] = index;
- sort(arr, low, i - 1);
- sort(arr, i + 1, height);
- }
- }
基准点选取:
基准点的选取一般有两种方式:
(1)选取待排序数组的第一个元素,中间元素,最后一个元素,取三者的中间元素。
(2)取low和height的一个随机数,left<m<height,取arr[m]为基准点,这种方式称为随机的快速排序。
时间复杂度分析:
最坏情况:即每一次排序之后,分成的数组,以基准点为分界,左边或右边的数组为空,而另一边,只比排序之前少了一项。也就是说,选择的基准点恰好是数组中最大的元素或者最小的元素。这时就要经过n次比较,和n次移动,T(n)=(n-1)+(n-2)+..........+1,所以时间复杂度为O(n^2)。
最好情况:即每一次排序之后,分成的数组,以基准点为分界,左边或右边的数组元素个数相等或者只相差一个。也就是说,选择的基准点恰好是数组元素的中间值。这时的递归的深度为O(log N),每次处理需要n次计算(比较+移动),所以时间复杂度为O(nlogn)。
空间复杂度分析:
快速排序的过程中,需要借助一个栈空间。在最好情况下,每次将数组分为等长的2部分,所以递归的深度为O(log N),空间复杂度为O(nlogn)。
在最坏情况下,每次快速排序,只能交换一个元素,退化为冒泡排序,所以空间复杂度为O(n^2)。