[Java 算法] 快速排序和快速选择排序(※)

练习一 : 颜色分类

75. 颜色分类 - 力扣(LeetCode)

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)

练习二 : 排序数组(快排)

912. 排序数组 - 力扣(LeetCode)

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;
    }
}
相关推荐
To_OC2 小时前
LC 200 岛屿数量:经典 DFS 入门题,我第一次写居然连方向都搞错了
javascript·算法·leetcode
To_OC19 小时前
LC 128 最长连续序列:别上来就排序,O (n) 解法才是这题的灵魂
javascript·算法·leetcode
刘马想放假1 天前
Modbus 全栈技术解析:TCP、RTU、ASCII、RTU over TCP
数据结构·网络协议
05Kevin1 天前
lk每日冒险题--数据结构6.27
算法
To_OC2 天前
从一次栈溢出报错说起,我把递归彻底扒明白了
javascript·算法·程序员
千纸鹤安安2 天前
千问Qwen-AgentWorld来了:一个语言模型搞定七大Agent场景,GPT-5.4都输了
算法
七牛开发者2 天前
MCP 到底是什么?为什么 Agent 都想接上它
算法·aigc·agent