基本思想:(分治)
- 先从数列中取出一个数作为key值;
- 将比这个数小的数全部放在它的左边,大于或等于它的数全部放在它的右边;
- 对左右两个小数列重复第二步,直至各区间只有1个数。
图解:
- 荷兰国旗问题
给定一个数组arr,和一个数num,请把小于num的数放在数组的 左边,等于num的数放 在数组的中间,大于num的数放在数组的 右边。要求额外空间复杂度O(1),时间复杂度 O(N)
private static void partition(int[] arr, int leftBound, int rightBound, int num) {
int left = leftBound;
int right = rightBound;
int i = 0;
while (i != right){
if (arr[i] < num){
swap(arr,i++,left++);
}else if (arr[i] > num){
swap(arr,i,right--);
}else {
i++;
}
}
}
static void swap(int[] arr,int i,int j){
int temp=arr[i];
arr[i]=arr[j];
arr[j]=temp;
}
小于 基数的时候 i 的值与 left 交换,且 i++ left++
大于 基数的时候 i 的值与 right 值交换, 且 right--, i 不变,因为 i 的值是从后面换上来的,还没有进行比较
等于的时候 i++
- 快排
随机快速排序(改进的快速排序) 1)在数组范围中,等概率随机选一个数作为划分值,然后把数组通过荷兰国旗问题分 成三个部分: 左侧<划分值、中间==划分值、右侧>划分值 2)对左侧范围和右侧范围,递归执行 3)时间复杂度为O(N*logN)
// arr[1..r] 排好序
public static void quickSort1(int[] arr, int l, int r) {
if (l < r){
swap(arr,r,l + (int) (Math.random() * (r - l + 1))); // 产生随机数
int[] p = partition(arr,l,r); // 这里的数组只有两个值,返回的是左边界和右边界
quickSort1(arr,l,p[0] -1); // 小于区域递归
quickSort1(arr,p[1] + 1,r);// 大于区域递归
}
}
// 这是一个处理arr[1..r]的函数
// 默认以arr[r]做划分,arr[r] -> p < p ==p >p
// 返回等于区域(左边界,右边界),返回一个长度为2的数组res,res[0],res[1]
public static int[] partition(int[] arr, int l, int r) {
int less = l -1;
int more = r;
while (l < more){
if (arr[l] < arr[r]){
swap(arr,++less,l++);
} else if (arr[l] > arr[r]) {
swap(arr,--more,l);
}else{
l++;
}
}
swap(arr,more,r); // 将基数换到中间
return new int[]{less + 1,more};// 返回左边界和右边界
}
static void swap(int[] arr,int i,int j){
int temp=arr[i];
arr[i]=arr[j];
arr[j]=temp;
}