当前位置: 首页 > news >正文

单调栈题目:找出最具竞争力的子序列

文章目录

  • 题目
    • 标题和出处
    • 难度
    • 题目描述
      • 要求
      • 示例
      • 数据范围
  • 解法
    • 思路和算法
    • 代码
    • 复杂度分析

题目

标题和出处

标题:找出最具竞争力的子序列

出处:1673. 找出最具竞争力的子序列

难度

6 级

题目描述

要求

给你一个整数数组 nums \texttt{nums} nums 和一个正整数 k \texttt{k} k,返回 nums \texttt{nums} nums 的长度为 k \texttt{k} k 且最具竞争力的子序列。

数组的子序列是从数组中删除一些元素(可能不删除元素)得到的序列。

在子序列 a \texttt{a} a 和子序列 b \texttt{b} b 第一个不相同的位置上,如果 a \texttt{a} a 中的数字小于 b \texttt{b} b 中对应的数字,那么我们称子序列 a \texttt{a} a 比子序列 b \texttt{b} b(相同长度下)更具竞争力。 例如, [1,3,4] \texttt{[1,3,4]} [1,3,4] [1,3,5] \texttt{[1,3,5]} [1,3,5] 更具竞争力,在第一个不相同的位置,也就是最后一个位置上, 4 \texttt{4} 4 小于 5 \texttt{5} 5

示例

示例 1:

输入: nums   =   [3,5,2,6],   k   =   2 \texttt{nums = [3,5,2,6], k = 2} nums = [3,5,2,6], k = 2
输出: [2,6] \texttt{[2,6]} [2,6]
解释:在所有可能的子序列集合 {[3,5],   [3,2],   [3,6],   [5,2],   [5,6],   [2,6]} \texttt{\{[3,5], [3,2], [3,6], [5,2], [5,6], [2,6]\}} {[3,5], [3,2], [3,6], [5,2], [5,6], [2,6]} 中, [2,6] \texttt{[2,6]} [2,6] 最具竞争力。

示例 2:

输入: nums   =   [2,4,3,3,5,4,9,6],   k   =   4 \texttt{nums = [2,4,3,3,5,4,9,6], k = 4} nums = [2,4,3,3,5,4,9,6], k = 4
输出: [2,3,3,4] \texttt{[2,3,3,4]} [2,3,3,4]

数据范围

  • 1 ≤ nums.length ≤ 10 5 \texttt{1} \le \texttt{nums.length} \le \texttt{10}^\texttt{5} 1nums.length105
  • 0 ≤ nums[i] ≤ 10 9 \texttt{0} \le \texttt{nums[i]} \le \texttt{10}^\texttt{9} 0nums[i]109
  • 1 ≤ k ≤ nums.length \texttt{1} \le \texttt{k} \le \texttt{nums.length} 1knums.length

解法

思路和算法

这道题中,竞争力顺序和字典序相似。要得到数组 nums \textit{nums} nums 的长度为 k k k 的最具竞争力的子序列,等价于从数组 nums \textit{nums} nums 中删除 nums . length − k \textit{nums}.\textit{length} - k nums.lengthk 个元素,使得剩下的 k k k 个元素形成的子序列是竞争力顺序最靠前的。记 remove = nums . length − k \textit{remove} = \textit{nums}.\textit{length} - k remove=nums.lengthk,则 remove \textit{remove} remove 是需要删除的元素个数。

基于上述分析,这道题和「移掉 K 位数字」相似。

如果只从数组 nums \textit{nums} nums 中删除一个元素,使得剩下的子序列为竞争力顺序最靠前,则应该使子序列中靠前的数字尽可能小。可能有两种情况,以下是两种情况和对应的策略:

  1. 存在下标 i i i 使得 nums [ i ] > nums [ i + 1 ] \textit{nums}[i] > \textit{nums}[i + 1] nums[i]>nums[i+1],则找到满足该要求的最小的下标 i i i,将下标 i i i 处的元素删除之后,剩下的子序列为竞争力顺序最靠前;

  2. 不存在下标 i i i 使得 nums [ i ] > nums [ i + 1 ] \textit{nums}[i] > \textit{nums}[i + 1] nums[i]>nums[i+1],则将 nums \textit{nums} nums 的最后一个元素删除,剩下的子序列为竞争力顺序最靠前。

根据上述策略执行 remove \textit{remove} remove 次删除元素操作,得到的子序列即为最具竞争力的子序列。

由于数组 nums \textit{nums} nums 的长度最大为 1 0 5 10^5 105,因此模拟上述操作的做法会超出时间限制,需要优化。

由于上述策略优先考虑删除左边的元素,因此可以从左到右遍历 nums \textit{nums} nums,在遍历过程中进行删除操作,只能删除 remove \textit{remove} remove 个元素。为了使得删除操作符合上述策略,可以使用单调栈,单调栈存储数字,满足从栈底到栈顶的数字单调递增。

从左到右遍历数组 nums \textit{nums} nums,对于每个元素,进行如下操作:

  1. 如果栈不为空、栈顶元素大于当前元素且已经删除的元素个数小于 remove \textit{remove} remove,则将栈顶元素出栈,重复该操作直到上述条件不满足(三个条件之一不满足即停止);

  2. 将当前元素入栈。

遍历结束之后,如果已经删除的元素个数小于 remove \textit{remove} remove,则需要继续删除元素,删除元素的方法是将栈顶元素出栈,直到删除的元素个数达到 remove \textit{remove} remove。此时,栈内元素按照从栈底到栈顶的顺序构成的子序列为数组 nums \textit{nums} nums 的长度为 k k k 的最具竞争力的子序列。

以下是示例 2 的计算过程,其中 num = [ 2 , 4 , 3 , 3 , 5 , 4 , 9 , 6 ] \textit{num} = [2, 4, 3, 3, 5, 4, 9, 6] num=[2,4,3,3,5,4,9,6] k = 4 k = 4 k=4,需要删除的元素个数 remove = 4 \textit{remove} = 4 remove=4

  1. 下标 0 0 0 处的元素是 2 2 2,将 2 2 2 入栈, stack = [ 2 ] \textit{stack} = [2] stack=[2],其中左边为栈底,右边为栈顶。

  2. 下标 1 1 1 处的元素是 4 4 4,将 4 4 4 入栈, stack = [ 2 , 4 ] \textit{stack} = [2, 4] stack=[2,4]

  3. 下标 2 2 2 处的元素是 3 3 3,由于 4 > 3 4 > 3 4>3,因此将 4 4 4 出栈,将 3 3 3 入栈, stack = [ 2 , 3 ] \textit{stack} = [2, 3] stack=[2,3],共删除 1 1 1 个元素。

  4. 下标 3 3 3 处的元素是 3 3 3,将 3 3 3 入栈, stack = [ 2 , 3 , 3 ] \textit{stack} = [2, 3, 3] stack=[2,3,3],共删除 1 1 1 个元素。

  5. 下标 4 4 4 处的元素是 5 5 5,将 5 5 5 入栈, stack = [ 2 , 3 , 3 , 5 ] \textit{stack} = [2, 3, 3, 5] stack=[2,3,3,5],共删除 1 1 1 个元素。

  6. 下标 5 5 5 处的元素是 4 4 4,由于 5 > 4 5 > 4 5>4,因此将 5 5 5 出栈,将 4 4 4 入栈, stack = [ 2 , 3 , 3 , 4 ] \textit{stack} = [2, 3, 3, 4] stack=[2,3,3,4],共删除 2 2 2 个元素。

  7. 下标 6 6 6 处的元素是 9 9 9,将 9 9 9 入栈, stack = [ 2 , 3 , 3 , 4 , 9 ] \textit{stack} = [2, 3, 3, 4, 9] stack=[2,3,3,4,9],共删除 2 2 2 个元素。

  8. 下标 7 7 7 处的元素是 6 6 6,由于 9 > 6 9 > 6 9>6,因此将 9 9 9 出栈,将 6 6 6 入栈, stack = [ 2 , 3 , 3 , 4 , 6 ] \textit{stack} = [2, 3, 3, 4, 6] stack=[2,3,3,4,6],共删除 3 3 3 个元素。

  9. 遍历结束之后,还需要删除 1 1 1 个元素才能得到长度为 4 4 4 的子序列,因此将 6 6 6 出栈, stack = [ 2 , 3 , 3 , 4 ] \textit{stack} = [2, 3, 3, 4] stack=[2,3,3,4],共删除 4 4 4 个元素。

  10. 最具竞争力的子序列是 [ 2 , 3 , 3 , 4 ] [2, 3, 3, 4] [2,3,3,4]

代码

class Solution {
    public int[] mostCompetitive(int[] nums, int k) {
        int length = nums.length;
        int remove = length - k;
        Deque<Integer> stack = new ArrayDeque<Integer>();
        for (int i = 0; i < length; i++) {
            int num = nums[i];
            while (!stack.isEmpty() && stack.peek() > num && remove > 0) {
                stack.pop();
                remove--;
            }
            stack.push(num);
        }
        while (remove > 0) {
            stack.pop();
            remove--;
        }
        int[] subsequence = new int[k];
        for (int i = k - 1; i >= 0; i--) {
            subsequence[i] = stack.pop();
        }
        return subsequence;
    }
}

复杂度分析

  • 时间复杂度: O ( n ) O(n) O(n),其中 n n n 是数组 nums \textit{nums} nums 的长度。需要遍历数组 nums \textit{nums} nums 进行删除 n − k n - k nk 个元素的操作,由于每个元素最多入栈和出栈各一次,其中最多有 n − k n - k nk 个数字出栈,因此时间复杂度是 O ( n + ( n − k ) ) = O ( n ) O(n + (n - k)) = O(n) O(n+(nk))=O(n)

  • 空间复杂度: O ( n ) O(n) O(n),其中 n n n 是数组 nums \textit{nums} nums 的长度。空间复杂度主要取决于栈空间,栈内元素个数不会超过 n n n

相关文章:

  • Python运算符,数字,字符串
  • JSP教学评估管理系统myeclipse开发mysql数据库bs框架java编程web网页结构
  • 【vue3】04. 跟着官网学习vue3
  • xv6源码阅读——xv6的启动,进程初识
  • 金仓数据库KingbaseES客户端应用参考手册--13. sys_isready
  • 前端工程师面试题总结
  • 从“1L 小钢炮”到 “PC界变形金刚”——Tiny助力企业数智转型的十年进化之路
  • 【数据结构:1.绪论】
  • 计算机组成原理第二章----数据信息的表示 详解版
  • 网络安全-防火墙安全加固
  • 中秋节祝福程序源代码分享:土地分类数据阈值筛选和重投影分类
  • Java新手小白入门篇 API - 多线程
  • Deep Reinforcement Learning with Double Q-learning(double DQN)
  • 【博客472】k8s中如何使用shared memory
  • SpringBoot2.6.8 多环境配置
  • “Material Design”设计规范在 ComponentOne For WinForm 的全新尝试!
  • Angular6错误 Service: No provider for Renderer2
  • Centos6.8 使用rpm安装mysql5.7
  • SpiderData 2019年2月25日 DApp数据排行榜
  • TCP拥塞控制
  • 表单中readonly的input等标签,禁止光标进入(focus)的几种方式
  • 从零开始在ubuntu上搭建node开发环境
  • 我建了一个叫Hello World的项目
  • 限制Java线程池运行线程以及等待线程数量的策略
  • 小程序滚动组件,左边导航栏与右边内容联动效果实现
  • 写代码的正确姿势
  • 一、python与pycharm的安装
  • 在weex里面使用chart图表
  • 自制字幕遮挡器
  • 小白应该如何快速入门阿里云服务器,新手使用ECS的方法 ...
  • ​什么是bug?bug的源头在哪里?
  • ​无人机石油管道巡检方案新亮点:灵活准确又高效
  • # C++之functional库用法整理
  • #pragma once
  • #pragma once与条件编译
  • #在线报价接单​再坚持一下 明天是真的周六.出现货 实单来谈
  • $.ajax()
  • (+4)2.2UML建模图
  • (1)(1.13) SiK无线电高级配置(五)
  • (a /b)*c的值
  • (python)数据结构---字典
  • (安卓)跳转应用市场APP详情页的方式
  • (附源码)spring boot北京冬奥会志愿者报名系统 毕业设计 150947
  • (附源码)小程序儿童艺术培训机构教育管理小程序 毕业设计 201740
  • (六)Hibernate的二级缓存
  • (六)vue-router+UI组件库
  • (推荐)叮当——中文语音对话机器人
  • (轉貼)《OOD启思录》:61条面向对象设计的经验原则 (OO)
  • .cfg\.dat\.mak(持续补充)
  • .net core 依赖注入的基本用发
  • .Net CoreRabbitMQ消息存储可靠机制
  • .net Stream篇(六)
  • .NET/C# 避免调试器不小心提前计算本应延迟计算的值
  • .NET/C# 检测电脑上安装的 .NET Framework 的版本
  • .NET版Word处理控件Aspose.words功能演示:在ASP.NET MVC中创建MS Word编辑器