分治法Q3——快速排序
快速排序写法一:
package 分治;
public class 快速排序 {
/**快速排序法 分治实现
* 重点在分区根据分区方法不同分为:单向扫描法 双向扫描法
* template:1.terminator 2.process(prepare data,split your big problem)
* 3.drill down ,merge 4.reverse the current level states
* quickSort(nums,begin,end)
* -->subproblem:
* 主元元素下标为mid 分为[begin,mid-1] [mid+1,end]
* -->subresult:
* quickSort(nums,begin,mid-1) quickSort(nums,mid+1,end)
*
*
*/
/**
* 对[begin,end]区间元素排序
*/
void quickSort(int []nums,int begin,int end) {
if(begin<end) {//terminator 当区间只有一个元素时
//求主元位置 分区
int position=partion_bilateral(nums,begin,end);
quickSort(nums,begin,position-1);
quickSort(nums,position+1,end);
}
}
//分区法一:单向扫描法
public int partion_single(int[] nums,int begin,int end) {
int pivot=nums[begin];
int sp=begin+1; //扫描指针 自左向右扫描待排序元素
int bigger=end;//右侧指针
while(sp<=bigger) {
if(nums[sp]<=pivot) {//扫描元素不大于主元
sp++;
}else {//扫描元素大于主元
swap(nums,sp,bigger);
bigger--;
}
}
swap(nums,bigger,begin);
// System.out.print(nums[bigger]+": ");
// for (int i : nums) {
// System.out.print(i+" ");
// }
System.out.println();
return bigger;
}
//分区法二:双向扫描法
public int partion_bilateral(int[] nums,int begin,int end) {
int pivot=nums[begin];
int left=begin+1; //左侧扫描指针 自左向右扫描待排序元素
int right=end;//右侧扫描指针 自右向左扫描待排序元素
//右侧指针扫到左侧指针左面则终止扫描
while(left<=right) {
//发现第一个大于主元的元素退出第一层while循环 left不动
//考略极端情况下 数组全部有序 left一直++到越界 出现访问过界
//所以应该先把left<=right放前面 简单与&& 回优先判掉left越界的情况 不进行nums[left]<=pivot的访问
while(left<=right&&nums[left]<=pivot) left++;
//发现第一个小于主元的元素退出第一层while循环 right不动
while(left<=right&&nums[right]>pivot) right--;
//两个指针还没错位时 交换指针的元素
if(left<right) {
swap(nums,left,right);
}
}
//扫描结束 右侧指针的位置就是主元应该待的位置
swap(nums,begin,right);
System.out.println(nums[right]);
return right;
}
public void swap(int[] array,int i,int j) {
int temp=array[i];
array[i]=array[j];
array[j]=temp;
}
public static void main(String[] args) {
int begin=0;
int []nums=new int[] {8,9,6,2,1,7,5,3,10,4};
//int []nums=new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int end=nums.length-1;
快速排序 a=new 快速排序();
a.quickSort(nums,begin,end);
for (int i : nums) {
System.out.print(i+" ");
}
}
}
在所选取的轴点左右元素数量比较均匀的情况下
同时也是最好时间复杂度:T(n)=2*(n/2)+O(n) 为O(n*logn)
在全部有序情况下,此时没有达到分区的效果 每次切出来的区段为一个元素
同时也是最坏时间复杂度;T(n)=T(n-1)+O(n) 为O(n^2)
空间复杂度:为递归调用次数logn*辅助空间O(1) O(logn)
由递推式推出时间复杂度参见我之前文章算法Day2——递归笔记
单向扫描法
快速排序写法二:
package suanfa3;
import java.util.Arrays;
public class QuickSort {
//[0,7]
public static void quickSort(int a[] ,int l,int r) {
if(l>r)
return;
int pivot=partion(a, l, r);
//System.out.println(l+" "+r+" "+pivot);
quickSort(a, l, pivot-1);
quickSort(a, pivot+1, r);
}
//找到基准元素的位置
public static int partion(int a[],int l,int r) {
int i=l;
int target=a[i];
//双指针扫描交换
while(l<r) {
//从右往左扫 遇到第一个小于基准元素的r指针就停下来
while(l<r&&a[r]>=target) {
r--;
}
//从左往右扫 遇到第一个大于基准元素的l指针就停下来
while(l<r&&a[l]<=target) {//等于的时候指针也要移动
l++;
}
//交换两指针停下来位置的元素 同时指针各自移动“前进”以进行新一轮扫描
if(l<r) {
int tmp=a[l];
a[l]=a[r];
a[r]=tmp;
}
//System.out.println(Arrays.toString(a));
}
a[i]=a[l];
a[l]=target;
//System.out.println(Arrays.toString(a));
return l;
}
public static void main(String[] args) {
int a[ ]={8,4,3,7,1,5,6,2};
//int []a={5,4,3,9,1,5,6,2};
quickSort(a, 0, a.length-1);
System.out.println(Arrays.toString(a));
}
}
排序过程:
排序结果:
随机化的快速排序算法
package suanfa3;
/**
* @author JohnnyLin
* @version Creation Time:2020年9月26日 上午9:58:01
* 类说明
*/
import java.util.Arrays;
import java.util.Random;
public class QuickSort_random {
static Random random = new Random();
//[0,7]
public static void quickSort(int a[] ,int l,int r) {
if(l>r)
return;
int pivot=partion(a, l, r);
//System.out.println(l+" "+r+" "+pivot);
quickSort(a, l, pivot-1);
quickSort(a, pivot+1, r);
}
//找到基准元素的位置
public static int partion(int a[],int l,int r) {
int x=random.nextInt(r - l + 1) + l;
//基准元素值
int target=a[x];
//保留区间第一个元素下标
int left=l;
//初始化 交换该区间第一个元素与基准元素
int t=a[l];
a[l]=target;
a[x]=t;
//双指针扫描交换
while(l<r) {
//从右往左扫 遇到第一个小于基准元素的r指针就停下来
while(l<r&&a[r]>=target) {
r--;
}
//从左往右扫 遇到第一个大于基准元素的l指针就停下来
while(l<r&&a[l]<=target) {//等于的时候指针也要移动
l++;
}
//交换两指针停下来位置的元素
if(l<r) {
int tmp=a[l];
a[l]=a[r];
a[r]=tmp;
}
}
a[left]=a[l];
a[l]=target;
return l;
}
public static void main(String[] args) {
//int a[ ]={8,4,3,7,1,5,6,2};
//int []a={5,4,3,9,1,5,6,2};
int a[]=new int[1000];
//产生1-100的逆序数组用于测试
for(int i=0;i<1000;i++)
a[i]=1000-i;
double startTime = System.currentTimeMillis();
for(int i=0;i<=100;i++)
quickSort(a, 0, a.length-1);
double endTime = System.currentTimeMillis();
System.out.println("随机数快速排序时间:"+(endTime-startTime)/100);
System.out.println(Arrays.toString(a));
}
}
分别对优化前和优化后的快速排序算法进行测试: