快速排序
归并排序与快速排序都使用了「分治思想」
归并排序
拆分:不管数组的形态,总是将数组一分为二;
组合:合并两个有序的数组。
快速排序
拆分:根据某个元素 pivot,将数组整理成两个部分; 前半部分小于 pivot, 后半部分大于等于 pivot; 把 pivot 交换到前半部分的最后一个元素。
组合:什么都不用做。
快速排序的基本想法
快速排序每一次都排定一个元素(这个元素呆在了它最终应该呆的位置),然后递归地去排它左边的部分和右边的部分,依次进行下去,直到数组有序。
快速排序的算法思想
分而治之(分治思想),与「归并排序」不同,「快速排序」在「分」这件事情上不想「归并排序」无脑地一分为二,而是采用了 拆分 的方法,因此就没有「合」的过程。
演示代码:
import java.util.Arrays;
public class Quicksort {
public static int[] sortArray(int[] nums) {
int len = nums.length;
quickSort(nums, 0, len - 1);
return nums;
}
private static void quickSort(int[] nums, int left, int right) {
if(left< right){
int pIndex = partition(nums, left, right);
quickSort(nums, left, pIndex - 1);
quickSort(nums, pIndex + 1, right);}
}
private static int partition(int[] nums, int left, int right) {
// 基准值
int pivot = nums[left];
int lt = left;
// 循环不变量:
// all in [left + 1, lt] < pivot
// all in [lt + 1, i) >= pivot
for (int i = left + 1; i <= right; i++) {
if (nums[i] < pivot) {
lt++;
swap(nums, i, lt);
}
}
swap(nums, left, lt);
return lt;
}
private static void swap(int[] nums, int index1, int index2) {
int temp = nums[index1];
nums[index1] = nums[index2];
nums[index2] = temp;
}
public static void main(String[] args) {
int[] a = {4, 54, -93, -34, 45, 68, 13, 45, 7, 33, -54};
sortArray(a);
System.out.println(Arrays.toString(a));
}
}
快速排序优化
优化 1:
随机选择标定点元素,降低递归树结构不平衡的情况 由于快速排序在近乎有序的时候会非常差,此时递归树的深度会增加。此时快速排序的算法就退化为 O(N2);
解决办法:我们在每一次迭代开始之前,随机选取一个元素作为基准元素与第 1 个元素交换即可。
优化 2:
小区间使用插入排序
优化代码:
import java.util.Arrays;
import java.util.Random;
public class Quicksort {
private static final Random RANDOM = new Random();
private static final int INSERTION_SORT_THRESHOLD = 7;
public static int[] sortArray(int[] nums) {
int len = nums.length;
quickSort(nums, 0, len - 1);
return nums;
}
private static void quickSort(int[] nums, int left, int right) {
//优化一:小区间使用插入排序
if (right - left <= INSERTION_SORT_THRESHOLD) {
insertionSort(nums, left, right);
return;
}
int pIndex = partition(nums, left, right);
quickSort(nums, left, pIndex - 1);
quickSort(nums, pIndex + 1, right);
}
private static int partition(int[] nums, int left, int right) {
//优化二:随机选取一个元素作为基准元素与第 1 个元素交换
int randomIndex = RANDOM.nextInt(right - left + 1) + left;
swap(nums, left, randomIndex);
int pivot = nums[left];
int lt = left;
for (int i = left + 1; i <= right; i++) {
if (nums[i] < pivot) {
lt++;
swap(nums, i, lt);
}
}
swap(nums, left, lt);
return lt;
}
private static void swap(int[] nums, int index1, int index2) {
int temp = nums[index1];
nums[index1] = nums[index2];
nums[index2] = temp;
}
private static void insertionSort(int[] nums, int left, int right) {
for (int i = left + 1; i <= right; i++) {
int temp = nums[i];
int j = i;
while (j > left && nums[j - 1] > temp) {
nums[j] = nums[j - 1];
j--;
}
nums[j] = temp;
}
}
public static void main(String[] args) {
int[] a = {4, 54, -93, -34, 45, 68, 13, 45, 7, 33, -54};
sortArray(a);
System.out.println(Arrays.toString(a));
}
}