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

75 阅读4分钟

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

目录

[TOC]

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


问题分析

给定一个整数数组 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 是数组长度。

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

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

  1. 累加频率
  • 依次累加频率值,直到总和 ≥ 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)。

  1. 数组长度为奇数
  • 输入 [1,2,3,4,5] ,目标为 2.5 ,需删除两个元素(例如频率为2和1的元素)。

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


总结

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