基本思想
取待排序序列中任意一个元素作为分区点(一般取第一个元素或最后一个元素)。
按分区点将小于分区点的元素放在左边,将大于分区点的元素放在右边。
核心:分治。使用递归的方法,对分开的两块区域一直进行快速排序。直到区间缩小为1,就说明所有数据都有序了
取得分区点实现
数组array[low...high]:
将待排序数组的第一个元素array[low]作为分区点。
array[low+1...j]存放的是小于分区点的值,array[j+1...high]存放的是大于分区点的值。
然后,交换array[j]和分区点的值。
这样就能保证所有小于分区点的元素,都在分区点左侧;所有大于分区点的元素,都在分区点右侧。
最后,返回分区点下标。
实现代码
/**
* @Author: snayi
* @CreateTime: 2019-08-05 15:11
* @Description:
*/
public class QuickSort {
public static void quickInternal(int[] array) {
int n = array.length;
if (n <=1) {
return;
}
quickInternal(array,0,n-1);
}
/**
* 快排的递归实现
* @param array
* @param low 数组第一个元素下标
* @param high 数组最后一个元素下标
*/
public static void quickInternal(int[] array,int low,int high) {
//递归出口
if (low >= high) {
return;
}
//获取分区点
int pivot = partition(array,low,high);
//分区快排
quickInternal(array,low,pivot-1);
quickInternal(array,pivot+1,high);
}
/**
* 获得数组分区点
* @param array 带排序数组
* @param low 数组开始点下标
* @param high 数组结束点下标
* @return 数组分区点
*/
public static int partition(int[] array,int low,int high) {
//默认数组分区点为当前数组第一个元素,将其值保存下来
int pivot = array[low];
//小于分区点的最后一个值下标
int j = low;
//使用下标i遍历整个数组的值
for (int i = low+1; i <= high ; i++) {
//当前值小于分区点值
if (array[i] < pivot) {
//将当前值和j+1位置的值交换。让小于分区点的值增加一个
swap(array,j+1,i);
//小于分区点的值多了一个,j后移
j++;
}
}
//交换分区点的值,使其处在中间位置
swap(array,low,j);
//返回分区点下标
return j;
}
/**
*交换元素
*/
public static void swap(int[] array,int x,int y) {
int temp = array[x];
array[x] = array[y];
array[y] = temp;
}
public static void main(String[] args) {
int[] array = new int[]{8,10,2,3,6,1,5};
System.out.println("快排前的数组");
SortHelper.printArray(array);
System.out.println("快排后的数组");
quickInternal(array);
SortHelper.printArray(array);
}
}
指标分析
- 时间复杂度:
最好时间复杂度:分区均衡,O(nlogn)
最坏时间复杂度:分区极其不均衡(已经是有序数组,选择其中最大的数来分),O(nlogn)
平均时间复杂度:O(nlogn) - 空间复杂度: O(1)。原地排序,没有产生额外空间。
- 稳定性:不稳定
快排优化
二路快排
原始快排分区时没有考虑与分区点值相等的元素。
若等于分区值的重复元素太多,会导致分区不平衡,时间复杂度退化为O(n^2)。
实现思路
将小于分区点值(array[pivotIndex])和大于分区点值的元素放在数组的两端
双指针实现:
1.在当前数组中随机选取一个值作为分区点,然后将其与数组第一个元素array[low]交换
2.两个指针i、j,i从low+1开始向后遍历,j从high开始向前遍历
array[low+1...i-1]存放小于分区点的值,array[j+1...high]存放小于分区点的值
3.如果array[i]小于array[pivotIndex],则i向后走;否则i停下,等待与j交换值。
如果array[j]大于array[pivotIndex],则j向前走;否则j停下,等待与i交换值。
循环执行以上步骤。
4.如果i>j,则跳出循环。然后将j的值与low的值交换,返回分区点下标j。
- 当前数组
- i碰到了一个大于povit的值。j碰到了一个小于povit的值
- i和j对应的数据交换之后再继续循环