「归并排序」在「拆分子问题」环节是「无脑地」进行拆分,然后我们需要在「合」的环节进行一些操作。而「快速排序」在「分」这件事情上做出了文章,因此在「合」的环节什么都不用做。
「快速排序」大家可以阅读经典的算法教程《算法导论》《算法(第 4 版)》进行学习,也可以阅读 LeetBook 之 排序算法全解析 。我们在这里就不对「快速排序」算法进行具体讲解,而直接给出代码,相关的知识点通过注释给出。
这一版快速排序的代码,我们
参考代码 3:第 912 题:排序数组
Java
import java.util.Random;
public class Solution {
/**
* 随机化是为了防止递归树偏斜的操作,此处不展开叙述
*/
private static final Random RANDOM = new Random();
public int[] sortArray(int[] nums) {
int len = nums.length;
quickSort(nums, 0, len - 1);
return nums;
}
/**
* 对数组的子区间 nums[left..right] 排序
*
* @param nums
* @param left
* @param right
*/
private void quickSort(int[] nums, int left, int right) {
// 1. 递归终止条件
if (left >= right) {
return;
}
int pIndex = partition(nums, left, right);
// 2. 拆分,对应「分而治之」算法的「分」
quickSort(nums, left, pIndex - 1);
quickSort(nums, pIndex + 1, right);
// 3. 递归完成以后没有「合」的操作,这是由「快速排序」partition 的逻辑决定的
}
/**
* 将数组 nums[left..right] 分区,返回下标 pivot,
* 且满足 [left + 1..lt) <= pivot,(gt, right] >= pivot
*
* @param nums
* @param left
* @param right
* @return
*/
private int partition(int[] nums, int left, int right) {
int randomIndex = left + RANDOM.nextInt(right - left + 1);
swap(nums, randomIndex, left);
int pivot = nums[left];
int lt = left + 1;
int gt = right;
while (true) {
while (lt <= right && nums[lt] < pivot) {
lt++;
}
while (gt > left && nums[gt] > pivot) {
gt--;
}
if (lt >= gt) {
break;
}
// 细节:相等的元素通过交换,等概率分到数组的两边
swap(nums, lt, gt);
lt++;
gt--;
}
swap(nums, left, gt);
return gt;
}
private void swap(int[] nums, int index1, int index2) {
int temp = nums[index1];
nums[index1] = nums[index2];
nums[index2] = temp;
}
}
我们依然可以为「快速排序」增加打印输出语句:
参考代码 4:
Java
import java.util.Arrays;
import java.util.Random;
public class Solution {
/**
* 随机化是为了防止递归树偏斜的操作,此处不展开叙述
*/
private static final Random RANDOM = new Random();
public int[] sortArray(int[] nums) {
int len = nums.length;
quickSort(nums, 0, len - 1, 0);
return nums;
}
/**
* 对数组的子区间 nums[left..right] 排序
*
* @param nums
* @param left
* @param right
*/
private void quickSort(int[] nums, int left, int right, int recursionLevel) {
log("拆分子问题", left, right, recursionLevel);
// 1. 递归终止条件
if (left >= right) {
log("递归到底", left, right, recursionLevel);
return;
}
int pIndex = partition(nums, left, right);
// 2. 拆分,对应「分而治之」算法的「分」
quickSort(nums, left, pIndex - 1, recursionLevel + 1);
quickSort(nums, pIndex + 1, right, recursionLevel + 1);
// 3. 递归完成以后没有「合」的操作,这是由「快速排序」partition 的逻辑决定的
}
/**
* 将数组 nums[left..right] 分区,返回下标 pivot,
* 且满足 [left + 1..lt) <= pivot,(gt, right] >= pivot
*
* @param nums
* @param left
* @param right
* @return
*/
private int partition(int[] nums, int left, int right) {
int randomIndex = left + RANDOM.nextInt(right - left + 1);
swap(nums, randomIndex, left);
int pivot = nums[left];
int lt = left + 1;
int gt = right;
while (true) {
while (lt <= right && nums[lt] < pivot) {
lt++;
}
while (gt > left && nums[gt] > pivot) {
gt--;
}
if (lt >= gt) {
break;
}
// 细节:相等的元素通过交换,等概率分到数组的两边
swap(nums, lt, gt);
lt++;
gt--;
}
swap(nums, left, gt);
return gt;
}
private void swap(int[] nums, int index1, int index2) {
int temp = nums[index1];
nums[index1] = nums[index2];
nums[index2] = temp;
}
private void log(String log, int left, int right, int recursionLevel) {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(" ".repeat(Math.max(0, recursionLevel)));
stringBuilder.append(log);
stringBuilder.append(" ");
stringBuilder.append("=>");
stringBuilder.append(" ");
stringBuilder.append("[");
stringBuilder.append(left);
stringBuilder.append(", ");
stringBuilder.append(right);
stringBuilder.append("]");
System.out.println(stringBuilder.toString());
}
public static void main(String[] args) {
Solution solution = new Solution();
int[] nums = new int[]{7, 7, 7, 1, 7, 2, 3, 4, 4, 5, 5, 6, 7, 7, 8, 9};
int[] res = solution.sortArray(nums);
System.out.println(Arrays.toString(res));
}
}
控制台输出:
拆分子问题 => [0, 15]
拆分子问题 => [0, 9]
拆分子问题 => [0, 3]
拆分子问题 => [0, -1]
递归到底 => [0, -1]
拆分子问题 => [1, 3]
拆分子问题 => [1, 2]
拆分子问题 => [1, 0]
递归到底 => [1, 0]
拆分子问题 => [2, 2]
递归到底 => [2, 2]
拆分子问题 => [4, 3]
递归到底 => [4, 3]
拆分子问题 => [5, 9]
拆分子问题 => [5, 5]
递归到底 => [5, 5]
拆分子问题 => [7, 9]
拆分子问题 => [7, 8]
拆分子问题 => [7, 6]
递归到底 => [7, 6]
拆分子问题 => [8, 8]
递归到底 => [8, 8]
拆分子问题 => [10, 9]
递归到底 => [10, 9]
拆分子问题 => [11, 15]
拆分子问题 => [11, 14]
拆分子问题 => [11, 11]
递归到底 => [11, 11]
拆分子问题 => [13, 14]
拆分子问题 => [13, 13]
递归到底 => [13, 13]
拆分子问题 => [15, 14]
递归到底 => [15, 14]
拆分子问题 => [16, 15]
递归到底 => [16, 15]
[1, 2, 3, 4, 4, 5, 5, 6, 7, 7, 7, 7, 7, 7, 8, 9]
说明:
在程序中添加打印输出,是我们理解程序的重要方法。它虽然很粗暴,但很实用。
阅读量:580
点赞量:0
收藏量:0