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

每日算法-250407

记录一下今天刷的三道 LeetCode 题目。


2389. 和有限的最长子序列

题目

Problem 2389 Image

思路

排序 + 前缀和 + 二分查找

解题过程

  1. 理解题意: 题目要求我们对于 queries 数组中的每个查询值 q,找出 nums 数组中元素和 小于等于 q最长子序列 的长度。注意,是子序列,不是子数组,意味着我们可以任意选择元素,不必连续。
  2. 贪心策略: 为了让子序列尽可能长,我们应该优先选择 nums 中较小的元素。因此,第一步是对 nums 数组进行 排序
  3. 前缀和: 排序后,为了快速计算前 k 个最小元素的和,我们可以计算 nums 数组的 前缀和。我们将前缀和直接存储在排序后的 nums 数组中以节省空间(nums[i] 存储原 nums[0]nums[i] 的和)。
  4. 二分查找: 现在,对于每个查询 q = queries[i],我们需要在前缀和数组(即修改后的 nums 数组)中找到最大的索引 k,使得 nums[k] <= q。这等价于查找第一个 严格大于 q 的前缀和 nums[index] 的位置 index。这个 index 就是满足条件的最长子序列的长度(因为索引从 0 开始,index 个元素对应的前缀和下标是 index-1,所以第一个大于 q 的位置 index 正好是满足 <=q 的元素个数)。
  5. 实现: 我们可以使用二分查找(check 函数,寻找下界 lower_bound 的变种)来找到第一个大于 q 的位置。具体地,我们查找 q + 1 的下界,其返回的 left 值即为所求的长度 index
  6. 结果: 将每个查询得到的长度 index 存回 queries 数组的相应位置。

复杂度

  • 时间复杂度: O ( N log ⁡ N + M log ⁡ N ) O(N \log N + M \log N) O(NlogN+MlogN)
    • Nnums 的长度,Mqueries 的长度。
    • 排序 nums 需要 O ( N log ⁡ N ) O(N \log N) O(NlogN)
    • 计算前缀和需要 O ( N ) O(N) O(N)
    • 对于 M 个查询,每个查询进行一次二分查找,需要 O ( log ⁡ N ) O(\log N) O(logN)。总共是 O ( M log ⁡ N ) O(M \log N) O(MlogN)
    • 总时间复杂度为 O ( N log ⁡ N + M log ⁡ N ) O(N \log N + M \log N) O(NlogN+MlogN)
  • 空间复杂度: O ( 1 ) O(1) O(1) (或 O ( log ⁡ N ) O(\log N) O(logN) 取决于排序算法)
    • 我们在原地修改了 nums 数组来存储前缀和,并在原地修改了 queries 数组来存储结果,因此辅助空间复杂度是 O ( 1 ) O(1) O(1) (不考虑排序可能产生的栈空间)。

Code

class Solution {
    public int[] answerQueries(int[] nums, int[] queries) {
        Arrays.sort(nums);
        for (int i = 1; i < nums.length; i++) {
            nums[i] += nums[i - 1];
        }
        for (int i = 0; i < queries.length; i++) {
            int index = check(nums, queries[i] + 1);
            queries[i] = index;
        }
        return queries;
    }

    private int check(int[] arr, int k) {
        int left = 0, right = arr.length - 1;
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (arr[mid] < k) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
        return left;
    }
}

1385. 两个数组间的距离值

题目

Problem 1385 Image

思路

排序 arr2 + 二分查找

解题过程

  1. 理解题意: 我们需要计算 arr1 中有多少个元素 x,满足以下条件:对于 所有 arr2 中的元素 y,都有 |x - y| > d。换句话说,arr1 中的元素 x 如果要计入结果,那么在 arr2不能存在 任何元素 y 使得 |x - y| <= d,即不能存在 y 位于闭区间 [x - d, x + d] 内。
  2. 优化查找: 为了高效地检查 arr2 中是否存在位于 [x - d, x + d] 区间的元素,我们可以先对 arr2 排序
  3. 二分查找: 遍历 arr1 中的每个元素 x。对于当前的 x,我们要在已排序的 arr2 中查找是否存在元素 y 满足 x - d <= y <= x + d
    • 我们可以使用二分查找找到 arr2 中第一个 大于等于 x - d 的元素的位置 index(即查找 x - d 的下界 lower_bound)。
    • 如果 index == arr2.length,说明 arr2 中所有元素都小于 x - d,因此不可能有元素落在 [x - d, x + d] 区间内。此时 x 符合条件。
    • 如果 index < arr2.length,说明 arr2[index]arr2 中第一个(即最小的)大于等于 x - d 的元素。我们还需要检查这个元素是否也 小于等于 x + d
      • 如果 arr2[index] <= x + d,则说明 arr2[index] 正好落在 [x - d, x + d] 区间内,x 不符合条件。
      • 如果 arr2[index] > x + d,则说明 arr2 中最小的不小于 x - d 的元素都已经大于 x + d 了,因此 arr2 中没有元素落在 [x - d, x + d] 区间内。此时 x 符合条件。
  4. 计数: 维护一个计数器 ret,当 x 符合条件时(即 index == arr2.lengtharr2[index] > x + d),将其加一。
  5. 返回: 遍历完 arr1 后,返回 ret

复杂度

  • 时间复杂度: O ( M log ⁡ M + N log ⁡ M ) O(M \log M + N \log M) O(MlogM+NlogM)
    • Narr1 的长度,Marr2 的长度。
    • 排序 arr2 需要 O ( M log ⁡ M ) O(M \log M) O(MlogM)
    • 遍历 arr1 ( N 次),每次在 arr2 中进行二分查找,需要 O ( log ⁡ M ) O(\log M) O(logM)。总共是 O ( N log ⁡ M ) O(N \log M) O(NlogM)
    • 总时间复杂度为 O ( M log ⁡ M + N log ⁡ M ) O(M \log M + N \log M) O(MlogM+NlogM)
  • 空间复杂度: O ( 1 ) O(1) O(1) (或 O ( log ⁡ M ) O(\log M) O(logM) 取决于排序算法)
    • 排序 arr2 通常可以原地完成,二分查找使用常数额外空间。辅助空间复杂度为 O ( 1 ) O(1) O(1)

Code

class Solution {
    public int findTheDistanceValue(int[] arr1, int[] arr2, int d) {
        int ret = 0;
        Arrays.sort(arr2);
        for (int x : arr1) {
            int index = check(arr2, x - d);
            if (index == arr2.length || arr2[index] > x + d) {
                ret++;
            }
        }
        return ret;
    }

    private int check(int[] arr, int t) {
        int left = 0, right = arr.length - 1;
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (arr[mid] < t) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
        return left;
    }

}

2300. 咒语和药水的成功对数

题目

Problem 2300 Image

思路

二分查找

解题过程

  1. 理解题意: 给定咒语强度数组 spells 和药水强度数组 potions,以及一个成功阈值 success。对于每个咒语 spells[i],需要计算有多少种药水 potions[j] 使得它们的乘积 spells[i] * potions[j] >= success。返回一个数组 pairs,其中 pairs[i] 是第 i 个咒语对应的成功药水数量。
  2. 优化查找: 对于固定的 spells[i],我们需要在 potions 中找到所有满足 potions[j] >= success / spells[i]potions[j] 的数量。如果 potions 是无序的,我们需要遍历整个 potions 数组,时间复杂度较高。
  3. 排序与二分: 我们可以先对 potions 数组进行 排序。排序后,对于给定的 spells[i],所有满足条件的 potions[j] 会集中在数组的右侧。
  4. 查找边界: 我们需要找到第一个(即强度最小的)满足 spells[i] * potions[j] >= success 的药水 potions[j] 的索引 index。可以使用二分查找来完成。
    • 注意:乘积 spells[i] * potions[j] 可能 超过 int 的范围,因此在比较时应使用 long 类型进行计算。
    • 我们的目标是找到最小的 j 使得 (long)spells[i] * potions[j] >= success。这相当于在 potions 数组中查找 success / spells[i] (可能需要向上取整,或直接用乘法比较) 的下界 lower_bound
    • check 函数实现的正是找到第一个 mid 使得 (long)potions[mid] * x >= success 的位置 left
  5. 计算数量: 一旦找到这个最小索引 index,由于 potions 已经排序,从 potions[index] 到数组末尾的所有药水都将满足条件。因此,成功的药水数量就是 m - index,其中 mpotions 数组的长度。
  6. 结果: 对每个 spells[i] 重复步骤 4 和 5,将计算出的数量存入结果数组 ret 的对应位置。

复杂度

  • 时间复杂度: O ( M log ⁡ M + N log ⁡ M ) O(M \log M + N \log M) O(MlogM+NlogM)
    • Nspells 的长度,Mpotions 的长度。
    • 排序 potions 需要 O ( M log ⁡ M ) O(M \log M) O(MlogM)
    • 遍历 spells ( N 次),每次在 potions 中进行二分查找,需要 O ( log ⁡ M ) O(\log M) O(logM)。总共是 O ( N log ⁡ M ) O(N \log M) O(NlogM)
    • 总时间复杂度为 O ( M log ⁡ M + N log ⁡ M ) O(M \log M + N \log M) O(MlogM+NlogM)
  • 空间复杂度: O ( N ) O(N) O(N) (或 O ( N + log ⁡ M ) O(N + \log M) O(N+logM))
    • 需要一个长度为 N 的数组 ret 来存储结果。
    • 排序 potions 可能需要 O ( log ⁡ M ) O(\log M) O(logM) 的栈空间(取决于实现)。
    • 主要空间开销来自于结果数组,为 O ( N ) O(N) O(N)

Code

class Solution {
    public int[] successfulPairs(int[] spells, int[] potions, long success) {
        int n = spells.length, m = potions.length;
        int[] ret = new int[n];
        Arrays.sort(potions);
        for (int i = 0; i < n; i++) {
            long t = (success - 1) / spells[i];
            int index = check(potions, (int)t, m);
            ret[i] = m - index;
        }
        return ret;
    }

    private int check(int[] potions, int t, int m) {
        int left = 0, right = m - 1;
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (potions[mid] < t + 1) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
        return left;
    }
}

相关文章:

  • 数字经济产业标杆:树莓集团如何塑造产业服务价值体系
  • 没有独立显卡如何安装torch
  • 极简设计的力量:用 `apiStore` 提升项目效率与稳定性
  • oracle查询是否锁表了
  • Objective-C语言的编程范式
  • 昇腾910b多机部署deepseek-r1-w8a8量化全攻略
  • Hive 常见面试 300 问
  • leetcode 368. 最大整除子集 中等
  • Scala(六)
  • Matlab绘图—‘‘错误使用 plot输入参数的数目不足‘‘
  • 工程项目中通讯协议常见问题
  • 零代码构建AI知识库:基于亮数据网页抓取API的维基百科数据自动化采集实战
  • 昂贵的DOM操作:一次DOM导致的性能问题排查记录
  • 自动化测试是什么?Selenium实战!
  • 【分享开发笔记,赚取电动螺丝刀】使用STM32F103的hal库,采用PWM+DMA发送方式驱动WS2812的RGB彩灯
  • k8s介绍
  • Ansible 入门教程:从零开始掌握自动化运维
  • LiteFlow[规则引擎]简单介绍和它的设计模式
  • 【计网】HTTP协议 1.0/1.1/2.0详解
  • python分词工具汇总
  • 上海国际珠宝时尚功能区未来三年如何建设?六大行动将开展
  • 广西壮族自治区党委常委会:坚决拥护党中央对蓝天立进行审查调查的决定
  • 外交部部长助理兼礼宾司司长洪磊接受美国新任驻华大使递交国书副本
  • 刘小涛任江苏省委副书记
  • 乌克兰官员与法德英美四国官员举行会谈
  • 商务部:今年前3月自贸试验区进出口总额达2万亿元