一、回顾
- 分而治之框架
- 归并排序
- 将数组分解为一个一个只有一个元素的小数组,这些单元素小数组只有一个元素,天然便是有序的,这样一来我们就可以递归的合并这些小数组,直到排好序。
- 在前面我们分而治之策略的问题大部分都侧重于合并问题解的部分,今天我们说的问题则简化了合并部分,侧重分解问题。
二、快速排序
- 基本思想
- 任选元素x作为分界线,称为主元
- 交换重排,满足x左侧元素小于右侧
- 实现方法
- 选取固定位置主元x
- 维护两个部分右端点变量i,j
- 考察数组元素A[j],只和主元比较
- 若,则交换A[j]和A[i+1],i,j右移
- 若,则j右移
- 最后将主元放在中间作为分界线
- 我们分析最坏情况是如何产生的:我们之前每次都将末尾元素作为主元,如果每次我们划分后除了主元外的其余元素都在主元一侧。这种情况是非常罕见的,是针对性构造的,为了避免这种问题,我们每次可以随机选择主元,这样就无法针对性的构造最差情况。
package com.tiger.study;
// 快速排序:有一个数组,我们选择一个主元(可以固定的选,也可以随机选),然后我们将主元右边全部放置比主元小的数,左边放置比主元大的数,递归求解,最终合并就是有序的
import java.util.Random;
public class QuickSort {
public static void main(String[] args) {
int[] arr = {2, 6, 7, 1, 3, 5, 6, 4};
// int[] arr = {4, 3, 2, 1};
quickSort(arr, 0, arr.length - 1);
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
System.out.println("=====");
quickSortRandom(arr, 0, arr.length - 1);
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
// 固定主元
private static void quickSort(int[] arr, int left, int right) {
if (left >= right) return;
int mainEleIndex = partition(arr, left, right);
quickSort(arr, left, mainEleIndex - 1);
quickSort(arr, mainEleIndex + 1, right);
}
private static int partition(int[] arr, int left, int right) {
int mainEleIndex = right;
int i = left - 1, j = left;
while (j < right) {
if (arr[mainEleIndex] > arr[j]) {
int temp = arr[i + 1];
arr[i + 1] = arr[j];
arr[j] = temp;
j++;
i++;
} else {
j++;
}
}
int temp = arr[i + 1];
arr[i + 1] = arr[mainEleIndex];
arr[mainEleIndex] = temp;
return i + 1;
}
// 随机主元
private static void quickSortRandom(int[] arr, int left, int right) {
if (left >= right) return;
int mainEleIndex = partitionRandom(arr, left, right);
quickSort(arr, left, mainEleIndex - 1);
quickSort(arr, mainEleIndex + 1, right);
}
private static int partitionRandom(int[] arr, int left, int right) {
Random random = new Random();
int mainEleIndex = left + random.nextInt(right - left + 1);
int i = left - 1, j = left;
while (j < right) {
if (arr[mainEleIndex] > arr[j]) {
int temp = arr[i + 1];
arr[i + 1] = arr[j];
arr[j] = temp;
j++;
i++;
} else {
j++;
}
}
int temp = arr[i + 1];
arr[i + 1] = arr[mainEleIndex];
arr[mainEleIndex] = temp;
return i + 1;
}
}
三、第k小的数
package com.tiger.study;
// 问题描述:寻找数组中第k小的元素
public class SerachKMinValue {
public static void main(String[] args) {
int[] arr = {21, 17, 37, 28, 13, 14, 22, 52, 40, 24, 48, 24, 48, 4, 47, 8, 42, 18};
System.out.println(findKthMinValue(arr, 0, arr.length - 1, 5));
}
private static int findKthMinValue(int[] arr, int left, int right, int k) {
int x;
int mainValueIndex = partition(arr, left, right);
if (k == (mainValueIndex - left + 1)) {
x = arr[mainValueIndex];
} else if (k < (mainValueIndex - left + 1)) {
x = findKthMinValue(arr, left, mainValueIndex - 1, k);
} else {
x = findKthMinValue(arr, mainValueIndex + 1, right, k - (mainValueIndex - left + 1));
}
return x;
}
private static int partition(int[] arr, int left, int right) {
int mainValueIndex = right;
int i = left - 1, j = left;
while (j < right) {
if (arr[j] < arr[mainValueIndex]) {
int temp = arr[i + 1];
arr[i + 1] = arr[j];
arr[j] = temp;
i++;
j++;
} else {
j++;
}
}
int temp = arr[i + 1];
arr[i + 1] = arr[mainValueIndex];
arr[mainValueIndex] = temp;
return i + 1;
}
}