梳理:荷兰国旗问题+快排
一、
题目1:给定一个num,把一个数组小于等于num的放在左边,大于num的放在右边,返回分界点。
package Algothrims;
public class heLangFlag {
static int arr[]= {4,7,8,3,6,2,1,9,10,5};
public static void partition(int[] arr,int l,int r,int num){
int i,j;
int less=-1;//小于区域指针
for(i=l;i<r;i++) { //一轮过后能得到小于区域和大于区域的交界索引x;
if(arr[i]<num) {
swap(arr,less+1,i);
less++;
}
}
for(i=0;i<10;i++) {
System.out.print(arr[i]+" ");
}
System.out.println("less="+less);
}
public static void main(String[] args) {
partition(arr,0,9,5);
}
public static void swap(int[] arr,int l,int r){
int t;
t=arr[l];
arr[l]=arr[r];
arr[r]=t;
}
}
// 4 3 2 1 5 8 7 9 10 6 less=4
题目2:荷兰国旗问题:把数组中小于num的放左边,等于num的放中间,大于num的放右边。
package Algothrims;
public class heLangFlagII {
static int arr[]= {4,5,5,3,6,7,1,9,10,5};
public static void partition(int[] arr,int l,int r,int num){
int less=l-1;//小于区域指针
int more=r+1;//大于区域指针
int cur = l; //当前指针
while(cur < more) {
if(arr[cur]<num) {
swap(arr,++less,cur++);
}
else if(arr[cur]>num) {
swap(arr,--more,cur);
}
else cur++;
}
for(cur=0;cur<10;cur++) {
System.out.print(arr[cur]+" ");
}
System.out.print("less="+less+" "+"more="+more);
}
public static void main(String[] args) {
partition(arr,0,9,5);
}
public static void swap(int[] arr,int l,int r){
int t;
t=arr[l];
arr[l]=arr[r];
arr[r]=t;
}
}
//4 3 1 5 5 5 9 10 7 6 less=2 more=6
1. 经典快排
经典快排:将数组中最后的数作为x,把小于等于x的数放在左边,大于x放在右边,然后左右两部分分别递归。
2.荷兰国旗思路改进经典快排
将小于num的放左边,等于 mum放中间,大于区域放右边。省去了等于部分的遍历操作
codding……java
package Algothrims;
public class heLangFlagII {
static int arr[]= {4,5,5,3,6,7,1,9,10,5};
public static void quickSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
quickSort(arr, 0, arr.length - 1);
}
public static void quickSort(int[] arr, int L, int R) {
if (L < R) {
//swap(arr, L + (int) (Math.random() * (R - L + 1)), R);//随机快排
int[] p = partition(arr, L, R);
quickSort(arr, L, p[0]);
quickSort(arr, p[1] , R);
}
}
public static int[] partition(int[] arr,int L,int R){
int less = L-1;//小于区域指针
int more = R; //大于区域指针-->注意点1:最后一个数不参与交换
int cur = L; //当前指针
while(cur < more) {
if(arr[cur]<arr[R]) {
swap(arr,++less,cur++);
}
else if(arr[cur]>arr[R]) {
swap(arr,--more,cur);
}
else cur++;
}
swap(arr,more,R); //注意点2:因为我们的初始more是最后一个位置,意味着最后一个数是不参与交换的,所以在while结束后,因为前面的数都比它大,所以把该数与大于区域第一个数交换
return new int[] { less , more+1 };
}
public static void main(String[] args) {
quickSort(arr);
for(int i=0; i<10; i++) {
System.out.print(arr[i]+" ");
}
}
public static void swap(int[] arr,int l,int r){
int t;
t=arr[l];
arr[l]=arr[r];
arr[r]=t;
}
}
分析:现在我们需要从小到大排序,而数据是9,8,7,6,5,4,3,2,1,0;经典排序的时间复杂度就会变成O(n2), 跟数据分布有很大的关系。最好O(Nlog2N), 最坏:有序数据—O(n2), 平均:O(Nlog2N)
3. 随机快排
上3中,把注释打开就是一个随机快排
随机快排: 长期期望复杂度——O(NlogN),代码简单,常数项简单,很常用。该复杂度是经过数学证明的,我暂不做了解。
二、
-
快排《啊哈算法》的描述以及注意点
/* * 快速排序-两边一起走 * 以首或者尾作为key * 问题1:以首元素为key时,为什么一定要注意从右边开始移动? * 问题2:为什么左边的i=left+1会出问题?因为如果最后只剩两个数据,i就直接等于j,这就失去了意义。 */ public void quickSort(int left,int right) { int i=left; //i=left+1 int j=right; long temp; long key=arr[left]; if(left>=right) return; while(i!=j) //不等于退出,下面直接做交换 { while(arr[j]>=key && i<j) j--; while(arr[i]<=key && i<j) i++; //交换i,j索引数据 temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } arr[left] = arr[i]; arr[i] = key; quickSort(i+1,right); quickSort(left,i-1); } /** * 问题1:为什么key设为首值时,一定要右边先行 * 对这个程序来说,key设为最左边,是大区域往左走的趋势,所以j先开始 * 如果中间区域是小与key的,那么i=j之后交换 * 如果中间区域大于key的,继续往左走,大区域范围扩大。 * 如果key设为最右边,那么是小于区域往右扩大的趋势,从i先开始 * 为什么有这样的划分?那是因为这个程序在处理最后i=j的时候是要注意的。 * 前面已经设置了优先次序,所以最后可以这样直接处理。但这个不够通用? * 如果我们把key的值设为随机,或者中间呢? */
上述问题1的分析:上面的如果没有解释清楚可以仔细阅读下面的这个演示过程