[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;
    }
}
相关推荐
啦哈拉哈12 小时前
Leetcode题解记录-hot100(81-100)
算法·leetcode·职场和发展
csdn_aspnet12 小时前
Java 霍尔分区算法(Hoare‘s Partition Algorithm)
java·开发语言·算法
诸葛务农12 小时前
道路行驶条件下电动汽车永磁电机的有效使用寿命及永磁体的失效和回收再利用(下)
java·开发语言·算法
snow@li12 小时前
AI:理解 大数据、算法、算力、电力、生成式AI、token 之间的关系
大数据·人工智能·算法
小智老师PMP13 小时前
零基础能不能考PMP?零基础专属学习路径+全套扶持体系
学习·算法·职场和发展·软件工程·求职招聘·敏捷流程
Dillon Dong13 小时前
【风电控制】FPGA采集Vdc的ADC增益系数解析——从数字码到实际电压的桥梁
算法·fpga开发·变流器·风电控制
TDengine (老段)13 小时前
TDengine 压缩编码机制 — 双层压缩架构与类型特化算法
大数据·数据库·物联网·算法·时序数据库·tdengine·涛思数据
不会就选b13 小时前
数据结构之顺序表和链表的OJ题(上)
数据结构·链表
妄想出头的工业炼药师14 小时前
LVIO鲁棒
算法·开源
aini_lovee14 小时前
MATLAB 图像修复 — 偏微分方程方法
算法