快速排序的基本思想:
递归的实现上面的步骤,用归纳法证明是最终的结果一定是排序后的结果:如果左子数组和右子数组都是有序的,且切分元素在两者之间,那么最终的排序结果显而易见是有序的。
切分
递归前一个重要的步骤是要切分数组。
假设我们要排序的数组为[6,8,1,4,9,0,3,5,2,7],同时选定最左边的数为pivot,以pivot为基准不停的交换两侧的数:
从右到左,小于pivot的数标记为j,从左到右,大于pivot的数标记为i,然后交换i,j,不停的重复直到i>=j,最后交换pivot和i。
这个方法首先保证了pivot固定不动,但实际上pivot也可以参与到每次的交换中,只要最后的结果是切分的数组即可。
代码
package com.sort_algs;
import java.util.PriorityQueue;
import java.util.Scanner;
import static java.lang.Integer.parseInt;
import static java.lang.System.in;
import edu.princeton.cs.algs4.StdRandom;
//快速排序
public class Quicksort {
public static void sort(int[] a) {
StdRandom.shuffle(a); //打乱
sort(a,0,a.length -1);
}
private static void sort(int[] a,int lo, int hi) {
if (hi <= lo) return; //只有一个元素时
int j = partition1(a,lo,hi); //切分
sort(a, lo, j-1); //递归调用将左半部分a[lo,...,j-1] 排序
sort(a, j+1, hi); //递归调用将右半部分a[j+1,...,hi] 排序
}
//pivot随着每次交换
public static int partition(int[] a,int lo, int hi) {
int i = lo, j = hi;
int v = a[lo]; //pivot, 切分元素
while(i < j) {
while (i < j && a[j] > v) {
j--;
}
if(i < j) {
a[i] = a[j]; //v跟a[j]交换
i++;
}
while(i < j && a[i] < v) {
i++;
}
if(i < j) {
a[j] = a[i]; //i
j--;
}
}
a[i] = v;
return i;
}
//pivot先固定不动,最后交换
public static int partition1(int[] a,int low, int high) {
int i = low, j = high+1; //因为pivot选的左侧第一个数,因此从右到左时,最后一个数不能忽略,所以是j=high+1
int v = a[low]; //pivot, 切分元素
while(true) {
//跟上面的partition区别是不用判断i<j,如果判断了[0,2,1]这样的情况就无法正确切分
//
while(a[++i] < v) {
if(i == high) {
break;
}
}
while(a[--j] > v) {
if(j == low) {
break;
}
}
//
if(i >= j) {
break;
}
int temp = a[i];
a[i] = a[j];
a[j] = temp;
}
int temp = a[j];
a[j] = v;
a[low] = temp;
return j;
}
public static void main(String[] args) {
// Scanner sc = new Scanner(in);
// String[] str = sc.nextLine().replace("[", "").replace("]", "").split(",");
// int[] data = new int[str.length];
// for (int i = 0; i < data.length; i++) {
// data[i] = parseInt(str[i]);
// }
int[] a = {6,8,1,4,9,0,3,4,5,2,7};
Quicksort.sort(a);
for(int i = 0;i<a.length;i++) {
System.out.println(a[i]);
}
// sc.close();
}
}
查找第K大的元素
-
题目描述
给定一个无序的整型数组A[n],数组大小大于等于3,允许有值相同的元素;请设计算法找到该数组排序后第三大的元素值并输出. -
输入描述:
一个非空的整数数组(至少有3个元素,可正可负) -
输出描述:
第三大的元素值 -
示例1
- 输入
[3,2,1,5,6,4] - 输出
3
- 输入
-
示例2
- 输入
[1,1,2,2,3] - 输出
2
- 输入
首先这道题要在O(n)的条件下做出来。
查找第K大的元素就意味着排序,因此上面介绍的快速排序的优势就体现出来了。
快速排序需要对整个数组进行递归搜索+切分元素,但这里只需要针对第K大的元素,因此基于选择的快速排序就能在最优的时间得到答案。
package com.sort_algs;
import java.util.Scanner;
import static java.lang.System.in;
import static java.lang.Integer.parseInt;
public class FindN {
public static void main(String[] args) {
Scanner sc = new Scanner(in);
// String[] str = sc.nextLine().replace("[", " ").replace("]","").split(",");
// int[] data = new int[str.length];
// for (int i= 0;i<data.length;i++) {
// data[i] = parseInt(str[i]);
// }
// String str=sc.nextLine();
// String str1=str.toString().substring(1,str.length()-1);
// String[] str2=str1.split(",");
// int[] data=new int[str2.length];
// for(int i=0;i<str2.length;i++) {
// data[i]=Integer.parseInt(str2[i]);
// }
int[] data = {3,2,1,5,6,4};
int k = data.length-3+1;
//int I = findND(data,k);
int high = data.length - 1;
int I = findND_recursive(data, 0, high, k-1);
System.out.println(I);
sc.close();
}
public static int findND_recursive(int[] data, int begin, int high,int k) {
int pos = partition(data,begin,high);
if (pos == k) {
return data[pos];
}else if (pos < k) {
return findND_recursive(data, pos+1, high, k); //选择性尾递归。保证遇到合适的就返回,不会做多余的工作;
}else {
return findND_recursive(data, begin, pos-1, k);
}
}
public static int findND(int[] data,int k) {
int begin = 0;
int end = data.length-1;
int pos = 0;
while(begin <= end) {
pos = partition(data, begin , end);
if(pos == k-1) {
return data[pos];
}else if(pos > k-1) {
end = pos - 1;
}else {
begin = pos + 1;
}
}
return -1;
}
public static int partition(int[] data, int low, int high) {
int i = low;
int j = high + 1;
int v = data[low];
while(true) {
while(data[++i]<v) {
if(i==high) {
break;
}
}
while(data[--j]>v) {
if (j == low) {
break;
}
}
if(i>=j) {
break;
}
int temp = data[i];
data[i] = data[j];
data[j] = temp;
}
int temp = data[j];
data[j] = v;
data[low] = temp;
return j;
}
}
一个循环版本,一个递归版本。
参考
- https://wiki.jikexueyuan.com/project/easy-learn-algorithm/fast-sort.html
- http://data.biancheng.net/view/117.html
- 算法第4版. Robert Sedgwewick