快速排序:时间复杂度O(N*logN) 额外空间复杂度O(logN)
在一个无序数组arr中,先选择数组的最后一个值作为划分值end,将数组arr中小于划分值的放在左边,等于划分值的放中间,大于划分值的放右边。
可以通过时间复杂度O(N),额外空间复杂度O(1)完成上方的分组问题。首先假设两个虚拟组,左边是less组,有变量less指向less组的最右边位置,右边放more组,有变量more指向more组的最左边,其中划分值直接越过。然后有index从左至右逐个比较当前值与划分值的大小。
1:如果当前值小于划分值,当前值与less+1位置的值交换,less++;index++;
2:如果当前值等于划分值,直接index++;
3:如果当前值大于划分值,当前值与more-1位置的值交换,more--;index不变;
一直按照上面的分配规则,当index==more时令最后的划分值与more交换;
然后可以得到小于划分值的在arr[0]~arr[less]中,大于划分值的在arr[more+1]~arr[length-1];
然后左右分别递归,调用自己一直到index==end,就可以达到数组有序的目的。
public void quickSort(int[]arr){
if(arr==null||arr.length<2){
return;
}
quickSort(arr,0,arr.length-1);
}
public void quickSort(int []arr,int i,int j){
//递归是否进行的判断条件
if(i<j){
//随机快排 在i与j之间随机选择一个数与最后值交换,作为划分值,
//如果划分值选择出现很大的偏差,最差时间复杂度为O(N^2),,所以使用随机快排时间复杂度为O(N*logN)
swap(arr, i + (int) (Math.random() * (j - i + 1)), j);
int[]p=partition(arr,i,j);
//递归调用自己
quickSort(arr,i,p[0]);
quickSort(arr,p[1],j);
}
}
//荷兰国旗问题
public void partition(int []arr,int i,int j){
int less=i-1;
int more=j;
//单次排列是否进行的判断条件
while(i<more){
//按照规则排列
if(arr[i]<arr[j]){
swap(arr,++less,i++);
}else if(arr[i]==arr[j]){
i++;
}else{
swap(arr,--more,i);
}
}
swap(arr,more,j);
//将more和less组的边界值返回,作为下一次的j和i
return new int[]{less,more+1};
}
public void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
时间复杂度: T(n)=2T(n/2)【划分成两个n/2规模的子问题】+O(N)【partition过程】
估计同样规模递归行为的时间复杂度:master公式
T(N)=aT(N/b)+O(N^d);
如果:
1 log(b,a)>d T(N)为(N^(log(b,a));
2 log(b,a)==d T(N)为(N^d*logN);
3 log(b,a)<d T(N)为(N^d);
递归是将本身分成几个相同的小问题,调用自己的过程,而且在系统栈中是通过压栈和弹栈的方式实现的。把函数中申请的信息和运行到的行的信息压进栈,重复将子问题调用函数时的信息压入栈中,一直等到base case的触发然后依次弹栈,最后通过子问题的结果获得最终的结果。而父级函数按照栈中的记录继续向下运行。
额外空间复杂度O(logN)是在递归函数进入子函数时记录断点事需要的 而不是在partition函数中需要的