原理
- 从待排序区间选择一个基准值
如何选择基准值?- 可以选择两端的数据(代码中选取这种方法)
- 随机选取
- 三数取中法(后续会讲)
- 做 partition,将比基准值大的放在基准值的右边,比基准值小的放在基准值的左边
partition 的方法?(代码讲解)- hoare
- 挖坑法
- 遍历法
- 采用分治思想,对左右两个小区间按照同样的方式处理,直到小区间的长度为 1 ,代表有序,或者长度为 0 ,代表没有数据
分治方法?- 递归
- 非递归
实现
public class QuickSort{
public static void quickSort(int[] arr){
quickSortInter(arr,0,arr.length-1);
}
//分治思想:递归
public static void quickSortInter(int[] a,int left,int right){
if(left>=right){
return;
}
//1. 选择基准值 a[left]
//2. 做 partition(比基准值大的放在基准值右边,比基准值小的放在基准值左边)
int pivotIndex = partition(a,left,right);
//3. 对左右小区间继续处理
//左区间[left,pivotIndex-1]
//右区间[pivotIndex+1,right]
quickSortInter(a,left,pivotIndex-1);
quickSortInter(a,pivotIndex+1,right);
}
//做 partition 之 hoare 法
//将第一个数作为基准值,将数组最后一个元素和基准值比较,
//大于基准值,end--,小于基准值,再从数组第二个元素开始,
//如果元素小于基准值,begin++,
//如果元素大于基准值,将这个数和 end 指向的数交换,就达到了前面的数比基准值小,后面的数比基准值大,
//重复此步骤,直到 begin 和 end 相遇,交换基准值和 begin(或 end,此时 begin 和 end 已经相等)下标对应的元素
public static int partition(int[] a,int left,int right){
int begin = left;
int end = right;
int pivot = a[left];
while(begin < end){
while(begin < end && a[end] >= pivot){
end--;
}
while(begin < end && a[begin] <= pivot){
begin++;
}
//如果是内层的第一个循环走到这里,说明 a[end] < pivot,为了保证后面的元素比基准值大,所以需要交换
//如果是内层的第二个循环到这里,说明 a[begin] > pivot,为了保证前面的元素比基准值小,所以需要交换
swap(a,begin,end);
}
//说明 begin==end ,此时需要交换 begin 和 left 的值,
//保证基准值在中间,左边的元素比基准值小,右边的元素比基准值大
swap(a,left,begin);
return begin;
}
//做 partition 之挖坑法
//挖坑法和 hoare 相同的,只是不再交换,而是赋值
public static int partition(int[] a,int left,int right){
int begin = left;
int end = right;
int pivot = a[left];
while(begin < end){
while(begin < end && a[begin] <= pivot){
begin++;
}
a[end] = a[begin];
while(begin < end && a[end] >= pivot){
end--;
}
a[begin] = a[end];
}
a[begin] = pivot;
return begin;
}
//遍历
//上边两种方法都是从两头比较,而这种方法是从一边比较
//当所比较的元素小于基准值时,交换 i 和 d 的元素
public static int partition(int[] a,int left,int right){
int pivot = a[left];
int d = left + 1;
for(int i = left + 1;i<=right;i++){
if(a[i] < pivot){
swap(a,i,d++);
}
}
swap(a,left,d-1);
return d - 1;
}
//非递归实现分治思想
public static void quickSort(int[] arr){
Stack<Integer> stack = new Stack<>();
stack.push(arr.length-1);
stack.push(0);
while(!stack.isEmpty()){
int left = stack.pop();
int right = stack.pop();
if(left >= right){
continue;
}
}
int pivotIndex = patition(arr,left,right);
//3. 对左右小区间继续处理
//左区间[left,pivotIndex-1]
//右区间[pivotIndex+1,right]
stack.push(right);
stack.push(pivotIndex+1);
stack.push(left);
stack.push(pivotIndex-1);
}
基准值之三数取中法
在快排的过程中,要选取基准值,以基准值为分割点,将数据划分成小区间,在此采用三数取中法,即取前端、中间和后端三个数
再对左右区间进行类似操作即可得到有序序列
复杂度
稳定性
不稳定