先上代码
import java.util.Arrays;
import java.util.Random;
public class A008_快排的优化_1_三点中值法 {
static int cnt = 0;
public static void main(String[] args) {
int len = 2000;
int max = 1000000000;
int[] arr = getRandomArr(len, max);
System.out.println(Arrays.toString(arr));
quicksort(arr, 0, len - 1);
System.out.println(cnt + "正常的| " + Arrays.toString(arr));
arr = getRandomArr(len, max);
System.out.println(Arrays.toString(arr));
cnt = 0;
quicksortINmid(arr, 0, len - 1);
System.out.println(cnt + "三点中值法| " + Arrays.toString(arr));
}
public static void quicksortINmid(int[] arr, int left, int right) {
if (left >= right)
return;
int mid = (left + right) / 2;
int midValue = -1;
if (arr[left] <= arr[mid] && arr[left] >= arr[right]) {
midValue = left;
} else if (arr[right] <= arr[mid] && arr[right] >= arr[left]) {
midValue = right;
} else
midValue = mid;
int t = arr[midValue];
arr[midValue] = arr[left];
arr[left] = t;
int head = arr[left];
int L = left;
int R = right;
while (L < R) {
cnt++;
while (L < R && head < arr[R])
R--;
while (L < R && head >= arr[L])
L++;
t = arr[L];
arr[L] = arr[R];
arr[R] = t;
}
arr[left] = arr[R];
arr[R] = head;
quicksortINmid(arr, left, R - 1);
quicksortINmid(arr, R + 1, right);
}
public static void quicksort(int[] arr, int left, int right) {
if (left >= right)
return;
int head = arr[left];
int L = left;
int R = right;
while (L < R) {
cnt++;
while (L < R && head < arr[R])
R--;
while (L < R && head >= arr[L])
L++;
int t = arr[L];
arr[L] = arr[R];
arr[R] = t;
}
arr[left] = arr[R];
arr[R] = head;
quicksort(arr, left, R - 1);
quicksort(arr, R + 1, right);
}
public static int[] getRandomArr(int len, int max) {
int[] arr = new int[len];
Random ran = new Random(max);
for (int i = 0; i < len; i++) {
arr[i] = ran.nextInt(max) + 1;
}
return arr;
}
}
这是运行结果:
我的数组用的是一个随机生成的数组。数组比较长,就不截全了,主要的是文字前面的cnt的值,首先是正常的快排,取的主元是这一段里面的第一个元素,然后分别把左右指针,赋一个变量,L和R,我的快排是在《啊哈算法》一书中学的,作者讲的很形象,特别好理解:
他把指针比做了两个士兵,一个从右向左走,一个从左向右,从右向左的时候,只要发现当前R下标下的这个元素,小于主元,那么就停住,翻译成代码,也就是当R下标的元素,一直比主元大的时候,那么R就要一直–,(这里也是我一开始写快排,一直出错的地方,小于大于,傻傻的分不清)
然后当R这个士兵停住后,也就是退出循环了,那么,L这个士兵就开始向右走,那么道理也与R一样,当L下标的这个元素大于主元的时候,就停住,那么翻译成代码,也就是只要L下标的这个元素,一直小于等于主元的话,L就一直++
最后他们停住了,那么停住的时候,R下标的这个元素就是小于主元的,而L下标的这个元素是大于主元的,随后就进行交换,然后终止条件就是,当这两个相等的时候,我这里写的可能与其他人写的不太一样,没有增加判断条件,是因为我这里的L和R是不可能出现L>R的情况,最多就是L==R,这与3个while循环判断的条件有关;在跳出最大的循环后,就把主元和当前元素交换就行了,这里L和R可以随意用,因为L肯定是等于R的。
接下来讲三点中值法:
int mid = (left + right) / 2;
int midValue = -1;
if (arr[left] <= arr[mid] && arr[left] >= arr[right]) {
midValue = left;
} else if (arr[right] <= arr[mid] && arr[right] >= arr[left]) {
midValue = right;
} else
midValue = mid;
int t = arr[midValue];
arr[midValue] = arr[left];
arr[left] = t;
三点中值法的代码就这点,虽然多,也主要是if…else……语句那边多,三点中值法,其实就是在left、mid、right这三个点中,取一个中间的值的下标,仔细瞅一瞅代码,就能明白,他首先把left和mid和right进行比较,等价于=>(mid<=left<=right)剩下的如上所属。在确定主元后,我下面的三行交换,也是为了偷懒,因为先写好的快排不想修改,所以把中间的下标的那个值,和left进行交换,其他的没变化。