练习一 : 颜色分类

java
class Solution {
public void sortColors(int[] nums) {
int len = nums.length;
int left = -1;//0元素的最右端
int right = len;//2元素的最左端
for(int i = 0;i<right;){
if(nums[i] == 0){
swap(nums,++left,i++);
}else if(nums[i] == 1){
i++;
}else{
swap(nums,--right,i);
}
}
}
public void swap(int[] nums,int i,int j){
int tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}
}
算法原理 :
① 指针含义
i : 用来遍历数组
left : 用来标记 0 的最右端 , 包含 left
right : 用来标记 2 的最左端 , 包含 right
② 区间表示
0,left 为 0 ; left+1,i-1 为 1 ; i,right-1 为待排序元素 ; right,len-1 为 2
③ 交换元素

如果 i 位置为 0 : 需要交换 left+1 和 i , 此时如果 i==++left , 是自己根自己交换 ; 交换完成 i++ ; 注意 : 由于 left+1 位置为已经排好序了的元素 , 所以能让 i++
如果 i 位置为 1 : 仅需要将 i++
如果 i 位置为 2 : 需要交换 right-1 和 i , 此时如果 i == --right,也是自己自己跟自己交换 , 交换完成退出循环(因为此时 i == right)
练习二 : 排序数组(快排)

java
class Solution {
public int[] sortArray(int[] nums) {
qsort(nums,0,nums.length-1);
return nums;
}
public void qsort(int[] nums,int l,int r){
if(l>=r) return ;
int key = nums[new Random().nextInt(r-l+1)+l];
int left = l-1, right = r+1;
for(int i = l;i<right;){
if(nums[i]<key){
swap(nums,i++,++left);
}else if(nums[i] == key){
i++;
}else {
swap(nums,i,--right);
}
}
//划分为三块 [l,left] [left+1,right-1] [right , r]
qsort(nums,l,left);
qsort(nums,right,r);
}
public void swap(int[] nums,int i,int j){
int tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}
}
练习三 : 数组中的第 k 个最大的元素
215. 数组中的第K个最大元素 - 力扣(LeetCode)

java
class Solution {
public int findKthLargest(int[] nums, int k) {
return qsort(nums, 0, nums.length - 1, k);
}
// 快速选择:在 [l, r] 区间找第 k 大的数
public int qsort(int[] nums, int l, int r, int k) {
if (l == r) {
return nums[r];
}
// 随机基准(防止最坏情况)
int key = nums[new Random().nextInt(r - l + 1) + l];
int left = l - 1;
int right = r + 1;
// 三路划分:<key | =key | >key
// 这里 i 不能 <r,必须是 i<right!!!(你原代码的BUG)
for (int i = l; i < right; ) {
if (nums[i] < key) {
swap(nums, i++, ++left);
} else if (nums[i] == key) {
i++;
} else {
swap(nums, i, --right);
}
}
// 最终三块区域
// [l, left] < key
// [left+1, right-1] == key
// [right, r] > key
int bigSize = r - right + 1; // 大于key的数量(第k大优先看这里)
int midSize = right - left -1; // 等于key的数量
if (bigSize >= k) {
// 第k大在右侧大区
return qsort(nums, right, r, k);
} else if (bigSize + midSize >= k) {
// 第k大就在等于key的区间
return key;
} else {
// 第k大在左侧小区,更新k值
return qsort(nums, l, left, k - bigSize - midSize);
}
}
public void swap(int[] nums, int i, int j) {
int tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}
}
练习四 : 库存管理
LCR 159. 库存管理 III - 力扣(LeetCode)
java
class Solution {
public int[] inventoryManagement(int[] nums, int k) {
// 快速排序划分,将数组前k小的元素放到左侧
qsort(nums, 0, nums.length - 1, k);
int[] ret = new int[k];
for(int i = 0; i < k; i++)
ret[i] = nums[i];
return ret;
}
// 快速选择:在 [l, r] 区间划分出最小的 k 个数
public void qsort(int[] nums, int l, int r, int k) {
if (l >= r) { //
return ;
}
// 随机选取基准值,避免最坏时间复杂度
int key = nums[new Random().nextInt(r - l + 1) + l];
int left = l - 1;
int right = r + 1;
// 三路划分:< key | = key | > key
// 循环条件必须为 i < right,保证划分正确
for (int i = l; i < right; ) {
if (nums[i] < key) {
swap(nums, i++, ++left);
} else if (nums[i] == key) {
i++;
} else {
swap(nums, i, --right);
}
}
// 划分完成后形成三个区间
// [l, left] 小于 key
// [left+1, right-1] 等于 key
// [right, r] 大于 key
int lessSize = left - l + 1; // 小于基准值的元素个数
int midSize = right - left - 1; // 等于基准值的元素个数
if (k <= lessSize) {
// 最小的 k 个数全部在 小于区 中
qsort(nums, l, left, k);
} else if (k <= lessSize + midSize) {
// 最小的 k 个数包含在 小于区 + 等于区 中,直接结束
return ;
} else {
// 剩余部分需要在 大于区 中寻找
qsort(nums, right, r, k - lessSize - midSize);
}
}
public void swap(int[] nums, int i, int j) {
int tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}
}