力扣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。

总结

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

相关推荐
丁浩66639 分钟前
Python机器学习---2.算法:逻辑回归
python·算法·机器学习
伏小白白白1 小时前
【论文精度-2】求解车辆路径问题的神经组合优化算法:综合展望(Yubin Xiao,2025)
人工智能·算法·机器学习
无敌最俊朗@2 小时前
数组-力扣hot56-合并区间
数据结构·算法·leetcode
囚生CY2 小时前
【速写】优化的深度与广度(Adam & Moun)
人工智能·python·算法
码农多耕地呗2 小时前
力扣94.二叉树的中序遍历(递归and迭代法)(java)
数据结构·算法·leetcode
微笑尅乐3 小时前
BFS 与 DFS——力扣102.二叉树的层序遍历
leetcode·深度优先·宽度优先
懒羊羊不懒@3 小时前
Java基础语法—最小单位、及注释
java·c语言·开发语言·数据结构·学习·算法
白云千载尽4 小时前
leetcode 912.排序数组
算法·leetcode·职场和发展
哆啦刘小洋4 小时前
Tips:预封装约束的状态定义
算法
代码充电宝4 小时前
LeetCode 算法题【简单】290. 单词规律
java·算法·leetcode·职场和发展·哈希表