排序算法是比较常见的算法,道理也比较简单,双指针法。今天被朋友问了个舍伍德排序算法,也是快速排序那种形式。尴尬,第一次听说,特意学了一下。结合快速排序,写了三个方法。
第一种:
//不需要将基准值归位的快速排序
public static void quickSort(int []a,int left,int right) {
if(left>=right) return ;
int i = left ;//左指针
int j = right ;//右指针
int key = a[i];//基准值
while(i<j) {
//右指针先遍历,直到找到一个小于基准值的
while(i<j&&a[j]>=key) j--;
//交换右指针与左指针的值
if(i<j&&a[j]<key) {
int temp = a[i];
a[i] = a[j];
a[j] = temp;
}
//左指针遍历,直到找到一个大于基准值的
while(i<j&&a[i]<=key)i++;
//交换左右指针的值
if(i<j&&a[j]>key) {
int temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
//递归处理左右数组
quickSort(a,left,i-1);
quickSort(a,i+1,right);
}
第二种:
//需要将基准值归位的快速排序
public static void quickSort2(int []a,int left,int right) {
if(left>=right) return ;
int i = left ;//左指针
int j = right ;//右指针
int key = a[i];//基准值
while(i<j) {
//右指针先遍历,直到找到一个小于基准值的
while(i<j&&a[j]>=key) j--;
//左指针遍历,直到找到一个大于基准值的
while(i<j&&a[i]<=key)i++;
//交换左右指针的值
if(i<j) {
int temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
a[left]=a[j];
a[j] = key;
//递归处理左右数组
quickSort(a,left,i-1);
quickSort(a,i+1,right);
}
第三种:舍伍德算法
一般快速排序存在最好最坏情况。
最好情况:
当一组数无序,每次划分成两个数组时,都能均匀的分成两部分。这样最好情况复杂度为nlogn。()
最坏情况:
当一组数有序,每次划分都只能划分掉一个数,最终复杂度为n^2.
如1 2 3 4 5 第一次分为 1 2345
第二次分为 2 345
第三次分为 3 45
第四次 4 5
这样相当于要划分n次,每次比较数也看做n,则为n^2。
虽然快速排序平均复杂度可以看做nlogn.但算法复杂度与输入实例之间有关系,未必都是平均时间复杂度。
这就是要采用舍伍德快速排序的原因,消除掉算法复杂度与输入实例之间的关系。
//舍伍德快速排序算法(找到第K大的数)
public static int quickSort3(int []a,int k) throws Exception {
if(k>a.length||k<=0) throw new Exception("k值输入不在异常");
int l= 0;
int r = a.length-1;
while(true) {
if(l>=r) return a[l];
int i = l;
//随机获取一个基准
Random rnd = new Random();
int j = 1+rnd.nextInt(r-1);
//交换a[i]与基准的值,使得基准为左边第一个
swap(a,i,j);
j = r+1;
int pivot = a[l];
while(true) {
//找到大于pivot的值后,跳出循环
while(i<a.length-1&&a[++i]<pivot) ;
//找到小于pivot的值后,跳出循环
while(j>0&&a[--j]>pivot) ;
if(i>=j) break;
swap(a,i,j);
}
//第一次循环结束后,判断此时的j,处在第几大的位置
if(j-l+1 == k) return pivot;
//基准归位,因为此时j的值小于基准,而基准在第一个位置,下面
//两条语句交换完后,保证左边的数都小于基准,右边的都大于
a[l] = a[j];
a[j] = pivot;
//对子数组重新进行划分
if(j-l+1<k) {
k = k-j+l-1;
l=j+1;//如果左边的数小于K个,l=j+1从右边数组继续划分
}else r=j-1;
}
}