只是不愿随波逐流 ...|

lidongdongdong~

园龄:2年7个月粉丝:14关注:8

8、快速排序

内容来自刘宇波老师算法与数据结构体系课

为什么快速排序需要随机化

image

1、单路快速排序

image

/**
* 单路快速排序: O(N * logN)
* 当数组中的元素一致时退化为 O(n^2)
*/
public class QuickSort {
private static final Random RANDOM = new Random();
private QuickSort() {
}
/**
* 快速排序
*/
public static <E extends Comparable<E>> void sort(E[] arr) {
sort(arr, 0, arr.length - 1);
}
/**
* 快速排序 arr[l, r]
*/
private static <E extends Comparable<E>> void sort(E[] arr, int l, int r) {
if (r - l <= 15) {
InsertionSort.sort(arr, l, r);
return;
}
int p = partition(arr, l, r);
sort(arr, l, p - 1);
sort(arr, p + 1, r);
}
/**
* 单路快排
*/
private static <E extends Comparable<E>> int partition(E[] arr, int l, int r) {
int p = RANDOM.nextInt(r - l + 1) + l;
swap(arr, l, p);
E v = arr[l];
int j = l;
// arr[l + 1, j] < v, arr[j + 1, r] >= v
for (int i = l + 1; i <= r; i++) {
if (arr[i].compareTo(v) < 0) swap(arr, i, ++j);
}
swap(arr, l, j);
return j;
}
private static <E> void swap(E[] arr, int a, int b) {
E k = arr[a];
arr[a] = arr[b];
arr[b] = k;
}
}

2、双路快速排序

image

/**
* 双路快速排序: O(N * logN)
*/
public class QuickSort {
private static final Random RANDOM = new Random();
private QuickSort() {
}
/**
* 快速排序
*/
public static <E extends Comparable<E>> void sort(E[] arr) {
sort(arr, 0, arr.length - 1);
}
/**
* 快速排序 arr[l, r]
*/
public static <E extends Comparable<E>> void sort(E[] arr, int l, int r) {
if (r - l <= 15) {
InsertionSort.sort(arr, l, r);
return;
}
int p = partition(arr, l, r);
sort(arr, l, p - 1);
sort(arr, p + 1, r);
}
/**
* 双路快排
*/
public static <E extends Comparable<E>> int partition(E[] arr, int l, int r) {
int p = RANDOM.nextInt(r - l + 1) + l;
swap(arr, l, p);
E v = arr[l];
int p1 = l + 1;
int p2 = r;
// arr[l, p1 - 1] <= v, arr[p2 + 1, r] >= v
// 循环结束的 2 种情况: p1 > p2 和 p1 == p2
while (true) {
while (p1 <= p2 && arr[p1].compareTo(v) < 0) p1++; // arr[p1] >= v
while (p1 <= p2 && arr[p2].compareTo(v) > 0) p2--; // arr[p2] <= v
if (p1 >= p2) break; // 也可以理解为当 p1 >= p2 时, 就没必要 swap(arr, p1, p2) 了
swap(arr, p1++, p2--);
}
swap(arr, l, p2);
return p2;
}
public static <E> void swap(E[] arr, int a, int b) {
E k = arr[a];
arr[a] = arr[b];
arr[b] = k;
}
}

3、三路快速排序

image

/**
* 三路快速排序: O(N * logN)
* 所有元素都相同的数组 O(n)
*/
public class QuickSort {
private static final Random RANDOM = new Random();
private QuickSort() {
}
public static <E extends Comparable<E>> void sort(E[] arr) {
sort(arr, 0, arr.length - 1);
}
private static <E extends Comparable<E>> void sort(E[] arr, int l, int r) {
if (r - l <= 15) {
InsertionSort.sort(arr, l, r);
return;
}
int[] p = partition(arr, l, r);
sort(arr, l, p[0]);
sort(arr, p[1], r);
}
private static <E extends Comparable<E>> int[] partition(E[] arr, int l, int r) {
int p = RANDOM.nextInt(r - l + 1) + l;
swap(arr, l, p);
E v = arr[l];
int p1 = l;
int p2 = r + 1;
// arr[l + 1, p1] < v, arr[p2, r] > v
// for (int i = l + 1; i < p2; i++) {
// if (arr[i].compareTo(v) < 0) swap(arr, i, ++p1);
// else if (arr[i].compareTo(v) > 0) {
// swap(arr, i, --p2);
// i--;
// }
// }
// arr[l + 1, p1] < v, arr[p2, r] > v
int i = l + 1;
while (i < p2) {
if (arr[i].compareTo(v) < 0) swap(arr, ++p1, i++);
else if (arr[i].compareTo(v) > 0) swap(arr, --p2, i);
else i++;
}
swap(arr, l, p1);
return new int[]{p1 - 1, p2};
}
private static <E> void swap(E[] arr, int a, int b) {
E k = arr[a];
arr[a] = arr[b];
arr[b] = k;
}
}

4、Partition 总结

单路快速排序法:完全有序的数据退化,因此引入随机化,但是具有大量重复元素的数据退化
双路快速排序法:可以让一样的数据比较平均的分配到左右两边
三路快速排序法:对完全重复的数据排序,复杂度为 O(n)
单路 双路 三路
随机数据
有序数据
重复数据 × O(n)
public class Partition {
private static final Random RANDOM = new Random();
private Partition() {
}
/**
* 单路快排
*/
public static <E extends Comparable<E>> int partition1(E[] arr, int l, int r) {
int p = RANDOM.nextInt(r - l + 1) + l;
swap(arr, l, p);
E v = arr[l];
int j = l;
for (int i = l + 1; i <= r; i++) {
if (arr[i].compareTo(v) < 0) swap(arr, i, ++j);
}
swap(arr, l, j);
return j;
}
/**
* 双路快排
*/
public static <E extends Comparable<E>> int partition2(E[] arr, int l, int r) {
int p = RANDOM.nextInt(r - l + 1) + l;
swap(arr, l, p);
E v = arr[l];
int p1 = l + 1;
int p2 = r;
while (true) {
while (p1 <= p2 && arr[p1].compareTo(v) < 0) p1++;
while (p1 <= p2 && arr[p2].compareTo(v) > 0) p2--;
if (p1 >= p2) break;
swap(arr, p1++, p2--);
}
swap(arr, l, p2);
return p2;
}
/**
* 三路快排
*/
public static <E extends Comparable<E>> int[] partition3(E[] arr, int l, int r) {
int p = RANDOM.nextInt(r - l + 1) + l;
swap(arr, l, p);
E v = arr[l];
int p1 = l;
int p2 = r + 1;
int i = l + 1;
while (i < p2) {
if (arr[i].compareTo(v) < 0) swap(arr, ++p1, i++);
else if (arr[i].compareTo(v) > 0) swap(arr, --p2, i);
else i++;
}
swap(arr, l, p1);
return new int[]{p1 - 1, p2};
}
private static <E extends Comparable<E>> void insertionSort(E[] arr, int l, int r) {
for (int i = l + 1; i <= r; i++) {
E k = arr[i];
int j;
for (j = i; j - 1 >= l && arr[j - 1].compareTo(k) > 0; j--) {
arr[j] = arr[j - 1];
}
arr[j] = k;
}
}
private static <E> void swap(E[] arr, int a, int b) {
E k = arr[a];
arr[a] = arr[b];
arr[b] = k;
}
}

5、复杂度分析

image

image

6、标定点为 mid 的快速排序

public class QuickSortMid {
private QuickSortMid() {
}
/**
* 快速排序
*/
public static <E extends Comparable<E>> void sort(E[] arr) {
sort(arr, 0, arr.length - 1);
}
/**
* 快速排序 arr[l, r]
*/
private static <E extends Comparable<E>> void sort(E[] arr, int l, int r) {
if (l >= r) return;
int p = partition(arr, l, r);
sort(arr, l, p - 1);
sort(arr, p + 1, r);
}
/**
* 单路快排
*/
private static <E extends Comparable<E>> int partition(E[] arr, int l, int r) {
int mid = l + (r - l) / 2;
swap(arr, l, mid);
E v = arr[l];
int j = l;
// arr[l + 1, j] < v, arr[j + 1, r] >= v
for (int i = l + 1; i <= r; i++) {
if (arr[i].compareTo(v) < 0) swap(arr, i, ++j);
}
swap(arr, l, j);
return j;
}
private static <E> void swap(E[] arr, int a, int b) {
E k = arr[a];
arr[a] = arr[b];
arr[b] = k;
}
}
/**
* 针对以中间点为标定点的快速排序, 生成一个长度为 length 的特殊数组, 使得快速排序退化
*/
public static Integer[] generateSpecialArray(int length) {
Integer[] arr = new Integer[length];
generateSpecialArray(arr, 0, length - 1, 0);
return arr;
}
/**
* 递归非常灵活
*/
private static void generateSpecialArray(Integer[] arr, int l, int r, int value) {
if (l > r) return;
int mid = l + (r - l) / 2;
arr[mid] = value;
swap(arr, l, mid);
generateSpecialArray(arr, l + 1, r, value + 1);
swap(arr, l, mid);
}

7、Select K 与 Top K 问题

另一种思路,利用优先队列解决 Select K 与 Top K 问题

image

7.1、Select K 问题

215 - 数组中的第 K 个最大元素

/**
* 求数组升序排序好后, 从右往左数第 K 个元素, 它的索引是 length - K
* 复杂度: O(n)
*/
public class FindKthLargest {
public static int findKthLargest(int[] arr, int k) {
return selectK(arr, 0, arr.length - 1, arr.length - k, new Random());
}
private static int selectK(int[] arr, int l, int r, int k, Random random) {
int p = partition(arr, l, r, random);
if (p == k) return arr[p];
if (p < k) return selectK(arr, p + 1, r, k, random);
return selectK(arr, l, p - 1, k, random);
}
/**
* 双路快速排序
*/
private static int partition(int[] arr, int l, int r, Random random) {
int p = random.nextInt(r - l + 1) + l;
swap(arr, l, p);
int v = arr[l];
int p1 = l + 1;
int p2 = r;
while (true) {
while (p1 <= p2 && arr[p1] < v) p1++;
while (p1 <= p2 && arr[p2] > v) p2--;
if (p1 >= p2) break;
swap(arr, p1++, p2--);
}
swap(arr, l, p2);
return p2;
}
private static void swap(int[] arr, int a, int b) {
int k = arr[a];
arr[a] = arr[b];
arr[b] = k;
}
}

7.2、Top K 问题

剑指 Offer 40 - 最小的 K 个数

/**
* 求数组升序排序好后, 从左往右数共 K 个元素
* 复杂度: O(n)
*/
public class GetLeastNumbers {
public static int[] getLeastNumbers(int[] arr, int k) {
if (k == 0) return new int[0];
return selectK(arr, 0, arr.length - 1, k - 1, new Random());
}
private static int[] selectK(int[] arr, int l, int r, int k, Random random) {
int p = partition(arr, l, r, random);
if (p == k) return Arrays.copyOf(arr, k + 1);
if (p < k) return selectK(arr, p + 1, r, k, random);
return selectK(arr, l, p - 1, k, random);
}
/**
* 双路快速排序
*/
private static int partition(int[] arr, int l, int r, Random random) {
int p = random.nextInt(r - l + 1) + l;
swap(arr, l, p);
int v = arr[l];
int p1 = l + 1;
int p2 = r;
while (true) {
while (p1 <= p2 && arr[p1] < v) p1++;
while (p1 <= p2 && arr[p2] > v) p2--;
if (p1 >= p2) break;
swap(arr, p1++, p2--);
}
swap(arr, l, p2);
return p2;
}
private static void swap(int[] arr, int a, int b) {
int k = arr[a];
arr[a] = arr[b];
arr[b] = k;
}
}
posted @   lidongdongdong~  阅读(19)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
展开