快速排序题目SelectK问题(力扣75.颜色分类、力扣215.数组中的第K个最大元素、面试题17.14最小K个数)

力扣75.颜色分类

给定一个包含红色、白色和蓝色、共 n 个元素的数组 nums ,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。

我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。

必须在不使用库内置的 sort 函数的情况下解决这个问题。

class Solution { //时间复杂度为O(n)
    //其实,变量 zero 相当于我们在三路快速排序算法中的 lt;
    //变量 two 相当于我们在三路快速排序算法中的 gt。
    public void sortColors(int[] nums) {
        // nums[0...zero] == 0, nums[zero + 1, i] == 1, nums[two, n - 1] == 2
        // 定义三个指针:zero、i、two,分别表示0的最右边界、当前处理的元素、2的最左边界
        int zero = -1, i = 0, two = nums.length;
        while(i < two){// 当前处理元素的指针小于2的最左边界时,继续循环
            // 如果当前元素为0,将其与zero右边的元素交换,并将zero和i都向右移动一位
            if(nums[i] == 0){
                zero++;
                swap(nums,zero,i);
                i++;
            }
            // 如果当前元素为2,将其与two左边的元素交换,并将two向左移动一位
            else if (nums[i] == 2){ // 注意此时不需要i右移,因为交换后的元素还需要继续判断
                two --;
                swap(nums, i, two);
            }
            else{ //如果当前元素是1,不需要操作,直接继续向右遍历
                i ++;
            }
        }
    }
    // 交换数组中指定位置的两个元素
    private void swap(int[] nums, int i, int j){
        int t = nums[i];
        nums[i]= nums[j];
        nums[j] = t;
    }
}

关于SelectK问题:即在一个无序数组中,找出第K小(大)的元素

我们首先来封装一个 selectK 的方法。封装好了这个方法以后,这三个问题都可
以快速求解。
我们的 selectK 的接口是这样的:

// 在 arr[l...r] 的范围里求解整个数组的第 k 小元素并返回
// k 是索引,即从 0 开始计算
int selectK(int[] arr, int l, int r, int k, Random rnd)

因为我们的 partition 过程需要随机选取标定点,所以我们还需要传一个 Random(快排的优化)
类的对象 rnd。
定义好函数签名以后,下面我们来书写相应的逻辑。

  • 首先,selectK 的过程,我们就是要执行一遍 partition。在这里,我使用双路快速排序的 partition。(partition属于核心代码块要掌握)
  • 注意,因为在这个问题中,我们肯定我们处理的数据类型是 int,所以,在代码中,我不再使用泛型:
private int partition(int[] arr, int l, int r, Random rnd){
    // 生成 [l, r] 之间的随机索引
    int p = l + rnd.nextInt(r - l + 1);
    swap(arr, l, p);
    // arr[l+1...i-1] <= v; arr[j+1...r] >= v
    int i = l + 1, j = r;
    while(true){
    while(i <= j && arr[i] < arr[l])
        i ++;
    while(j >= i && arr[j] > arr[l])
        j --;
    if(i >= j) break;

    swap(arr, i, j);
    i ++;
    j --;
}
    swap(arr, l, j);
    return j;
}
private void swap(int[] arr, int i, int j){
    int t = arr[i];
    arr[i] = arr[j];
    arr[j] = t;
}

有了 partition,我们的 selectK 的主题逻辑非常简单。

首先,进行 partition,假设结果是 p。我们只需要将 k 和 p 做比较。

  • 如果 k == p,直接返回 arr[p] 即可;
  • 如果 k < p,在 arr[l, p - 1] 的范围继续找,即调用 selectK(arr, l, p - 1, k, rnd);
  • 如果 k > p,在 arr[p + 1, r] 的范围继续找,即调用 selectK(arr, p + 1, r, k, rnd);

就有了下面的代码:

private int selectK(int[] arr, int l, int r, int k, Random rnd){
int p = partition(arr, l, r, rnd);
if(k == p) return arr[p];
if(k < p) return selectK(arr, l, p - 1, k, rnd);
    return selectK(arr, p + 1, r, k, rnd);
}

这样,我们就完成了 select K 的过程。是不是非常简单!

下面,我们用我们写的 select K,先来解决 Leetcode 上第 215 号问题:

这个问题是求第 k 大元素,但是我们的 selectK 求得是第 k 小元素。怎么办?
非常简单,我们只需要在调用 select K 之前,将求第 k 大元素的这个 k,转换成
对应求的是第几小元素对应的索引就好了。
按照题目描述,如果 k 是 1,对应就是要找最大元素,那么相应的我们的
select K 的索引,就是 nums.length - 1。(如果10个数,K=1,第一个最大的数,就是SelectK索引为9的那个的元素)
如果 k 是 nums.length,其实就是求最小元素,那么相应的我们的 selectK 的
索引,就是 0。  (如果10个数,第10个最大的数,就是SelectK索引为0的那个的元素,最小值)
他们之间的转换关系是 nums.length - k。

力扣215.数组中的第K个最大元素 

给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。

请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。

import java.util.Random; //导入Random包
class Solution {
    public int findKthLargest(int[] nums, int k) {
        //只有两行,其他的内容全部复用我们上面实现的 selectK,是不是很酷?
        //我们的SelectK是第K最小元素,所以这里findKthLargest传入下标要处理一下
        //转换关系是 nums.length - k
        Random rnd = new Random();
        return selectK(nums, 0, nums.length - 1, nums.length - k, rnd);
    }

    //有了 partition,我们的 selectK 的主题逻辑非常简单。
    private int selectK(int[] arr, int l, int r, int k, Random rnd){
        //首先,进行 partition,假设返回值结果是 p。我们只需要将 k 和 p 做比较。
        int p = partition(arr, l, r, rnd); 
        //如果 k == p,直接返回 arr[p] 即可;
        if(k == p) return arr[p]; 
        //如果 k < p,在 arr[l, p - 1] 的范围继续找,即调用 selectK(arr, l, p - 1, k, rnd);
        if(k < p) return selectK(arr, l, p - 1, k, rnd); 
        //如果 k > p,在 arr[p + 1, r] 的范围继续找,即调用 selectK(arr, p + 1, r, k, rnd);
        return selectK(arr, p + 1, r, k, rnd);
}
    private int partition(int[] arr, int l, int r, Random rnd){
        // 生成 [l, r] 之间的随机索引
        int p = l + rnd.nextInt(r - l + 1);
        swap(arr, l, p);
        // arr[l+1...i-1] <= v; arr[j+1...r] >= v
        int i = l + 1, j = r;
        while(true){
            while(i <= j && arr[i] < arr[l])
                i ++;
            while(j >= i && arr[j] > arr[l])
                j --;
            if(i >= j) break;
            swap(arr, i, j);
            i ++;
            j --;
        }
        swap(arr, l, j);
        return j;
    }

    //数组指定索引,两数交换
    private void swap(int[] arr, int i, int j){
        int t = arr[i];
        arr[i] = arr[j];
        arr[j] = t;
    }
}

面试题17.14最小K个数&&剑指offer40&&LCR159.库存管理

 

下面,我们解决《剑指 Offer》上的 40 号问题,最小的 k 个数: 
https://leetcode-cn.com/problems/zui-xiao-de-kge-shu-lcof/ 
对于这个问题,我们只要使用上面的 selectK,找到第 k 小的数。
然后,selectK 的过程由于调用了 partiton,所以会调整整个数组的内容。
此时,这个第 k 小的 数的前面所有元素,就是整个数组最小的 k 个数字了。
 

这里要注意,我们求的第 k 小的数字,对应的索引是 k - 1,因为题目给的下标k从0开始

所以我们调用 selectK 的时候,需要传入的索引参数是 k - 1。 
另外,对于这个问题,k 可能取 0,此时,我们不需要执行 selectK,直接返回一 
个含有 0 个元素的new int[] 就好了。

怎么样,是不是很简单?具体过程如下:

import java.util.Arrays;
import java.util.Random;
class Solution {
    public int[] getLeastNumbers(int[] arr, int k) {
        if(k == 0) return new int[0];
        Random rnd = new Random();
        selectK(arr, 0, arr.length - 1, k - 1, rnd);
//Arrays.copyOf()是java.util.Arrays 类中的一个方法。它会创建一个指定长度的新数组,并将原始数组中的元素复制到新数组中。
        return Arrays.copyOf(arr, k); 
    }
    private int selectK(int[] arr, int l, int r, int k, Random rnd){
        int p = partition(arr, l, r, rnd);
        if(k == p) return arr[p];
        if(k < p) return selectK(arr, l, p - 1, k, rnd);
        return selectK(arr, p + 1, r, k, rnd);
    }
    private int partition(int[] arr, int l, int r, Random rnd){
        // 生成 [l, r] 之间的随机索引
        int p = l + rnd.nextInt(r - l + 1);
        swap(arr, l, p);
        // arr[l+1...i-1] <= v; arr[j+1...r] >= v
        int i = l + 1, j = r;
        while(true){
            while(i <= j && arr[i] < arr[l])
                i ++;
            while(j >= i && arr[j] > arr[l])
                j --;
            if(i >= j) break;
            swap(arr, i, j);
            i ++;
            j --;
        }
        swap(arr, l, j);
        return j;
    }
    private void swap(int[] arr, int i, int j){
        int t = arr[i];
        arr[i] = arr[j];
        arr[j] = t;
    }
}

如果让调用java里库的方法,先 排序 后取值就更容易,不过  有点easy,面试时可能不让用

//时间复杂度:O(nlog⁡n),其中n是数组arr的长度。算法的时间复杂度即排序的时间复杂度。
//空间复杂度:O(log⁡n),排序所需额外的空间复杂度为 O(log⁡n)。
class Solution {
    public int[] smallestK(int[] arr, int k) {
        int[] vec = new int[k];
        Arrays.sort(arr); //调用Array.sort方法,给arr从小到大排序,然后取前k个数
        for (int i = 0; i < k; ++i) {
            vec[i] = arr[i];
        }
        return vec; //返回对应数组
    }
}
我在这个问题下提交如上的代码,大概需要 7ms 左右的时间:
但是,使用我们的 selectK 的方式,大概 2ms 就能搞定。
其实,O(n) 的复杂度和 O(nlogn) 的复杂度,在大多数时候,尤其是面试或者算
法竞赛中,差距并不大。但是,当数据规模上来以后,还是能看出来的。如果
有兴趣的同学,可以在自己的计算机上,测试一下对于 100 万甚至是 1000 万级
别的数据规模,看看而这是不是差距更明显?

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/563904.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C语言(二维数组)

Hi~&#xff01;这里是奋斗的小羊&#xff0c;很荣幸各位能阅读我的文章&#xff0c;诚请评论指点&#xff0c;关注收藏&#xff0c;欢迎欢迎~~ &#x1f4a5;个人主页&#xff1a;小羊在奋斗 &#x1f4a5;所属专栏&#xff1a;C语言 本系列文章为个人学习笔记&#x…

C语言趣味代码(二)

1.珠玑妙算 1.1 介绍 《珠玑妙算》(Mastermind)是英国Invicta公司于1973年开始销售的一款益智游戏&#xff0c;据说迄今为止已经在全世界销售了5000万套。《珠玑妙算》于1974年获奖后&#xff0c;在1975年传入美国&#xff0c;1976年leslieH.Autl博士甚至还出版了一本名为The…

狗都不学系列——虚拟机的基本使用

前言 虚拟机&#xff08;Virtual Machine&#xff09;指通过软件模拟的具有完整硬件系统功能的、运行在一个完全隔离环境中的完整计算机系统。在实体计算机中能够完成的工作在虚拟机中都能够实现。 简单来讲就是我们可以通过虚拟机来安装各种不同的操作系统进行体验。 这次主…

SQL约束

文章目录 约束约束的分类&#xff1a;按照约束的作用效果不同唯一约束主键约束外键约束检查约束非空约束默认值约束 按照是否跟随列和字段属性来创建约束行级约束表级约束 创建约束创建唯一约束创建完表之后创建唯一约束创建表的同时创建唯一约束行级约束表级约束 创建主键约束…

记录一下hive启动metestore服务时报错

【背景说明】 之前hadoop有问题&#xff0c;把hadoop和MySQL删了重装&#xff0c;hive没有动&#xff0c;然后启hive的metastore服务的时候&#xff0c;显示找不到metastore数据库 【报错】 Caused by: java.lang.reflect.InvocationTargetExceptionat sun.reflect.Generated…

完成学校官网页面制作

<!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8"> <title>教务系统</title> <style> .wap{ margin:0 auto; width:955px; } .top{ height:150px; padding-left:85px; …

内旋风铣也挺有意思,不够还没搞透

内旋风铣&#xff0c;这一术语在机械制造业中并不陌生&#xff0c;它代表着一种高效且精确的加工方法。这一技术的名称“内旋风铣”便揭示了其两大核心特点&#xff1a;一是“内”&#xff0c;指的是在工件内部进行加工&#xff0c;通常涉及到难以触及的复杂曲面&#xff1b;二…

FebHost:CC域名商业和非商业使用的区别

在当今互联网的世界中&#xff0c;域名的选择不仅关乎一个网站的在线身份&#xff0c;更与其背后的商业策略紧密相连。.cc 顶级域&#xff08;TLD&#xff09;作为众多选择之一&#xff0c;其使用方式可分为商业和非商业两大类。 商业用途&#xff1a;当提及.cc域名的商业用途…

Windows安装ElasticsSearch详细指南(亲测)

一、安装jdk ElasticSearch是基于lucence开发的&#xff0c;也就是运行需要java jdk支持。所以要先安装JAVA环境。 由于ElasticSearch 5.x 往后依赖于JDK 1.8的&#xff0c;所以现在我们下载JDK 1.8或者更高版本。 下载JDK1.8,下载完成后安装。 二、安装ElasticSearch 1.El…

【UE5.1 C++】VS2022下载安装

目录 步骤 一、Visual Studio下载安装 二、Visual Studio Integration Tool插件安装 先看一下UE和VS的兼容性 &#xff08;虚幻5&#xff1a;为虚幻引擎C项目设置Visual Studio开发环境&#xff09; &#xff08;虚幻4&#xff1a;设置虚幻引擎的Visual Studio&#xff0…

OJ:寻找独一无二的数

目录 &#x1f3dd;1.问题描述&#xff1a; &#x1f3dd;2.分析问题&#xff1a; &#x1f3dd;3.最终代码&#xff1a; &#x1f3dd;1.问题描述&#xff1a; &#x1f3dd;2.分析问题&#xff1a; 先看看下面的代码的结果是多少&#xff1f; #include<stdio.h> in…

宝塔面板国际版aaPanel 精简版安装

宝塔面板国际版aaPanel 精简版安装 很多人都知道宝塔面板&#xff0c;但不知道宝塔面板还有英文版&#xff0c;宝塔面板英文版不是单纯的宝塔面板的翻译&#xff0c;而是根据老外的使用习惯及国外的网络环境做了一定的优化&#xff0c; 比如&#xff1a;去掉了手机号验证、去…

论文笔记;LargeST: A Benchmark Dataset for Large-ScaleTraffic Forecasting

Neurips 2023 1 intro 目前交通预测数据集的问题 规模小&#xff0c;通常只包含数百个节点和边在时间覆盖范围上存在严重不足&#xff0c;通常不超过6个月单个节点的元数据不足 ——> 提出了一个新的基准数据集LargeST 广泛的图大小&#xff0c;包括加利福尼亚州的8,600个…

向量的求导

参考&#xff1a; 向量的求导 向量内积求导

基于Vue3的openlayers地图显示

基于Vue3的openlayers地图显示 &#xff08;1&#xff09;接着上一篇将讲&#xff0c;在components文件夹下创建BaseMap.vue文件夹 &#xff08;2&#xff09;在App.vue文件里面引入BaseMap.vue文件&#xff0c;如下代码所示&#xff1a; &#xff08;3&#xff09;在BaseMa…

Rust异步编程简介

Rust异步编程简介 计算机已经尽可能快了。加快程序速度的一种方法是并行或并发执行操作。这两个术语之间有细微的区别。并行执行意味着我们同时在两个不同的 CPU 上执行两个不同的任务。并发执行意味着单个 CPU 通过交错执行这些任务&#xff0c;同时在多个任务上取得进展。 R…

【支付宝】对接手机网站支付踩坑点记录

前言 简单记录一下对接Wap支付的问题&#xff0c;alipay和wxpay认证过程差不多&#xff0c;有个体商户或企业即可&#xff0c;前者文档不易懂后者还好&#xff0c;但是wxpay门槛高&#xff0c;个人认为pc网站支付(native支付)就是为了收300认证费&#xff01; 应用公私钥 第一…

【数据结构】时间复杂度的例题

&#x1f381;个人主页&#xff1a;我们的五年 &#x1f50d;系列专栏&#xff1a;数据结构 &#x1f337;追光的人&#xff0c;终会万丈光芒 前言&#xff1a; 这篇文章是关于时间复杂度的一些例题&#xff0c;关于时间复杂度和空间复杂度和算法的计算效率的基本知识点我放在…

应变计技术解读:如何精确测量结构物的形变

振弦式应变计是一种用于精确测量结构物形变的高精度仪器。这种应变计利用了振弦原理&#xff0c;即物体的振动频率会因其尺寸、形状或应变状态的改变而改变。通过测量这种频率变化&#xff0c;振弦式应变计能够提供关于材料形变的详细信息&#xff0c;这在结构健康监测、工程试…

Apache POI报表统计

Apache POl是一个处理Miscrosoft Office各种文件格式的开源项目。简单来说就是&#xff0c;我们可以使用 POl 在 Java 程N序中对Miscrosoft Office各种文件进行读写操作。一般情况下&#xff0c;POI都是用于操作 Excel 文件。 导入Maven坐标&#xff1a; <dependency>&l…