力扣1338 === 贪心算法解决数组减半问题

目录

问题分析

方法思路:贪心算法

步骤分解

代码解释

复杂度分析

正确性证明

示例验证

边界情况

总结


要解决这个问题,我们需要找到最少需要删除的不同整数集合,使得剩余的元素个数不超过原数组的一半。以下是对该问题的详细分析和解决方案的逐步说明。


问题分析

给定一个整数数组 arr,要求删除一个元素集合,使得剩余的元素个数 不超过原数组长度的一半 。我们需要找到 删除集合的最小大小

关键点

  • 删除的元素应尽可能多地减少剩余元素的数量。

  • 优先删除出现频率高的元素,可以最快达到目标。


方法思路:贪心算法

贪心算法的核心思想是每一步选择当前最优解,最终得到全局最优解。在本问题中,最优策略是 优先删除出现频率最高的元素,因为这样能快速减少剩余元素的数量。

步骤分解
  1. 统计频率

    遍历数组,用哈希表统计每个元素的出现次数。
    例如:数组 [3,3,3,3,5,5,5,2,2,7] 的统计结果为:3→4次,5→3次,2→2次,7→1次。

  2. 按频率降序排序

    将哈希表中的频率值提取出来,按从大到小排序。
    上例排序后得到 [4, 3, 2, 1]

  3. 累加频率直到满足条件

    从最高频率开始累加,直到累加值 ≥ 数组长度的一半 。记录累加的步数,即为最少删除的集合大小。
    上例中,数组长度 n=10,目标为 n/2=5。累加 4+3=7,只需删除两个元素集合(3和5),剩余元素为 7(即 7 ≤5)。


复制代码
/**
 * @param {number[]} arr
 * @return {number}
 */
var minSetSize = function (arr) {
  let ans = 0; // 需要删除的元素个数
  let sum = 0; // 当前删除的元素个数
  const map = new Map();
  // 统计每个元素出现的次数
  for (let i = 0; i < arr.length; i++) {
    map.set(arr[i], (map.get(arr[i]) || 0) + 1);
  }
  // 将map按出现次数从大到小排序
  const sortedMap = Array.from(map.entries()).sort((a, b) => b[1] - a[1]);
  console.log(sortedMap);
  // 遍历排序后的map,计算需要删除的元素个数
  for (let i = 0; i < sortedMap.length; i++) {
    // 当前删除的元素个数
    sum += sortedMap[i][1];
    // 需要删除的元素个数
    ans++;
    // 如果当前删除的元素个数大于等于数组的一半,则返回需要删除的元素个数
    if (sum >= arr.length / 2) {
      return ans;
    }
  }
};

console.log(minSetSize([3, 3, 3, 3, 5, 5, 5, 8, 8, 7]));

代码解释

  1. 统计频率

    • 使用 Map 结构遍历数组,记录每个元素的出现次数。

    • 时间复杂度:O(n),其中 n 是数组长度。

  2. 排序频率

    • 将频率值转换为数组并按降序排序。

    • 时间复杂度:O(k log k),其中 k 是不同元素的数量。

  3. 累加频率

    • 依次累加频率值,直到总和 ≥ n/2

    • 时间复杂度:O(k)。

    • 例如:当 sum 达到或超过 target 时,立即返回 count


复杂度分析

  • 时间复杂度 :O(n + k log k)

    其中 n 是数组长度,k 是不同元素的数量。

    • 统计频率:O(n)

    • 排序频率:O(k log k)

    • 累加频率:O(k)

  • 空间复杂度 :O(k)

    用于存储哈希表和排序后的频率数组。


正确性证明

贪心策略的正确性可以通过以下思路证明:

  • 最优子结构:每次选择频率最高的元素,可以确保每一步删除操作是最优的。

  • 反证法:假设存在更优的策略,那么该策略必须包含一个比当前选择的频率更低的元素,但这会导致需要更多的删除次数,与假设矛盾。


示例验证

以输入 [3,3,3,3,5,5,5,2,2,7] 为例:

  1. 统计频率:3→4,5→3,2→2,7→1。

  2. 排序频率:[4, 3, 2, 1]。

  3. 累加过程

    • 第1步:sum=4,count=1(未达到目标5)。

    • 第2步:sum=7,count=2(满足条件,返回2)。


边界情况

  1. 所有元素相同

    • 输入 [2,2,2,2],直接删除集合 {2}(count=1)。
  2. 数组长度为奇数

    • 输入 [1,2,3,4,5],目标为 2.5,需删除两个元素(例如频率为2和1的元素)。
  3. 恰好达到目标

    • 输入 [1,1,1,2,2,3],目标为3。删除频率为3和2的元素,sum=3+2=5 ≥3,count=2。

总结

通过贪心算法,优先删除高频元素,我们能够以最小的删除次数将数组大小减半。该方法高效且直观,适用于大部分实际场景。

相关推荐
冲帕Chompa11 分钟前
代码随想录动态规划part02
算法·动态规划
Y1nhl30 分钟前
力扣hot100_技巧_python版本
开发语言·python·算法·力扣
风铃儿~31 分钟前
Java微服务流量控制与保护技术全解析:负载均衡、线程隔离与三大限流算法
java·分布式·算法·微服务·负载均衡
ytz020842 分钟前
讲解贪心算法
算法·贪心算法
Dovis(誓平步青云)1 小时前
【数据结构】励志大厂版·初阶(复习+刷题):复杂度
c语言·数据结构·经验分享·笔记·学习·算法·推荐算法
Jerry说前后端2 小时前
2025年第十六届蓝桥杯省赛C++ 研究生组真题
c++·算法·蓝桥杯
小文数模2 小时前
2025年认证杯数学建模竞赛A题完整分析论文(含模型、可运行代码)(共32页)
算法·数学建模·matlab
LuckyLay2 小时前
LeetCode算法题(Go语言实现)_45
算法·leetcode·golang
君义_noip2 小时前
信息学奥赛一本通 1498:Roadblocks | 洛谷 P2865 [USACO06NOV] Roadblocks G
c++·算法·图论·信息学奥赛