1、普通快速排序
/**
*实现快速排序(普通快速排序)
*对arr[l,,,,r]共n个元素进行排序
*用递归方法,O(nlogn)
*方法一
*缺点:在近乎有序的数组下,该快排比归并要慢很多
*因为每次排序后,左右两个子递归规模相差悬殊,构成的二叉树平衡因子没有归并好
*在完全有序时,退化为O(n^2)
*优化:随机选取基准值
*/
public class QuickSort {
public void quickSort(int []arr,int n) {
__quickSort(arr,0,n-1);
}
private void __quickSort(int[] arr, int l, int r) {
//首先处理递归到底的情况,左边大于右边
//可以优化,如果对于小数组时,可以用插入排序完成
if(l>=r)
return;
int p=media(arr,l,r);
//对arr[l,,p-1]和arr[p+1,,,r]递归,每次比基准值小的在左,大的在右
__quickSort(arr, l, p-1);
__quickSort(arr, p+1, r);
}
//每次以第一个元素季即arr[l]为基准,比基准值小的在右,大的在左,
//返回基准值最终在数组中的位置
private int media(int[] arr, int l, int r) {
//
int v=arr[l];
int j=l;
//int temp=arr[l];
for(int i=l+1;i<=r;i++) {
//每次循环将小于v的往前换
if(arr[i]<v) {
swap(arr,i,j+1);
j++; //arr[j+1]始终是大于v的,arr[j]是最后一个<=v的
}
}
//再将基准值移动到中间
swap(arr,l,j);
//最终j所指的位置就是中间值
return j;
}
//可以用数组来作为引用
private void swap(int[] arr, int i, int j) {
if(i!=j) {
int temp =arr[j];
arr[j]=arr[i];
arr[i]=temp;
}
}
}
2、优化方法一
/**
*优化:将第一个元素为基准值改为随机一个元素为基准值
*降低退化为O(n^2)的概率
*数学期望值为O(nlogn)
*这样就不会受“几乎有序的数组”的干扰了
*但是对“几乎乱序的数组”的排序性能可能会稍微下降,至少多了排序前交换的那部分
*乱序时这个交换没有意义
*/
public class QuickSort2 {
public void quickSort2(int[]arr,int n) {
__quickSort2(arr,0,n-1);
}
private void __quickSort2(int[] arr, int l, int r) {
if(l>=r)
return;
int p=Media(arr,l,r);
__quickSort2(arr, l, p-1);
__quickSort2(arr, p+1, r);
}
private int Media(int[] arr, int l, int r) {
//double类型,产生一个l到r的随机数作为数组基准值下标
//将随机数与数组第一个数交换,即让随机数作为基准值
//减小近乎有序的概率
swap(arr, l, (int)Math.random()*(r-l+1)+l);
int v=arr[l];
int j=l;
for(int i=l+1;i<=r;i++) {
if(arr[i]<v) {
swap(arr,i,j+1);
j++;
}
}
swap(arr,l,j);
return j;
}
private void swap(int[] arr, int i, int j) {
if(i!=j) {
int temp =arr[j];
arr[j]=arr[i];
arr[i]=temp;
}
}
}
3、优化方法二【两路快排】
/**
*对于方一方二中如果存在大量重复元素,当基准值为重复元素时
*等于base的这些会聚集到右侧(或者稍微改改大小关系就会聚集到左侧)。
*总之就会聚集到一边。这样在数组中重复数字很多的时候,
*就又会导致两边子递归规模差距悬殊的情况,很有可能退化为O(n^2)
*这时想把等于base的那些数分派到base两边,而不是让他们聚集到一起。
*
*优化:可以将swap去掉
*/
public class QuickSort3 {
public void quickSort3(int[]arr,int n) {
__quickSort3(arr,0,n-1);
}
private void __quickSort3(int[] arr, int l, int r) {
if(l>=r)
return;
int p=Midal(arr,l,r);
__quickSort3(arr, l, p-1);
__quickSort3(arr, p+1, r);
}
private int Midal(int[] arr, int l, int r) {
swap(arr,l,(int)Math.random()*(r-l+1)+l);
//满足arr[l+1,i)<=v,arr(j,r]>=v
int v=arr[l];
int i=l+1,j=r;
while(true) {
//从左到右扫描,扫描出第一个比base大的元素,然后i停在那里
while(arr[i]<v&&i<r)//arr[i]不能=v,会导致v聚集在一边
i++;
//从右到左扫描,扫描出第一个比base小的元素,然后j停在那里
while(arr[j]>v&&j>=l)
j--;
if(i>=j)
break;
swap(arr, i, j);
i++;
j--;
}
//将基准值交换到合适位置
swap(arr, l, j);
return j;
}
private void swap(int[] arr, int i, int j) {
if(i!=j) {
int temp=arr[i];
arr[i]=arr[j];
arr[j]=temp;
}
}
}
4、优化方法三【三路快排】
/**
*当大量数据,且重复数多时,用三路快排
*将整个数组分为小于v,等于v,大于v三部分
*/
public class QuickSort4 {
public void quickSort4(int[]arr,int n) {
__quickSort4(arr,0,n-1);
}
private void __quickSort4(int[] arr, int l, int r) {
if(l>=r)
return;
//因为==v的部分是一个数组而非单独的一个数,所以直接处理,不调用函数
swap(arr,l,(int)Math.random()*(r-l+1)+l);
int v=arr[l];
//变量定义要保证初始空间为空
int i=l+1;//arr[lt+1,i)==v
int lt=l;//arr[l+1,lt]<v
int gt=r+1;//arr[gt,r]>v
while (i<gt) {
if(arr[i]==v) {
i++;
}else if (arr[i]<v) {
swap(arr, i, lt+1);//画示意图,arr[lt+1]==v
i++;
lt++;
}else {
swap(arr, i, gt-1);//arr[gt-1]为未处理的数据
gt--;
}
}
swap(arr, l, lt);
__quickSort4(arr, l, lt-1);
__quickSort4(arr, gt, r);
}
private void swap(int[] arr, int i, int j) {
if(i!=j) {
int temp=arr[i];
arr[i]=arr[j];
arr[j]=temp;
}
}
}