美化数组的最小删除次数;找最值的办法:力扣2216

题目回顾

给定一个整数数组 nums,每次可以从数组的左端右端删除一个元素。求最少需要删除多少个元素,才能将数组中的最大值最小值都删除掉。

核心思路:找到最大值和最小值的位置,然后从三种删除策略中取最小值。

这是一道简单题,但是找出位置的方法可以不同,用来巩固知识刚好。


三种删除策略

假设最小值和最大值的位置分别为 l 和 rl < r):

策略公式含义
策略1r + 1只从左边删除,直到删掉最大值位置
策略2n - l只从右边删除,直到删掉最小值位置
策略3l + 1 + n - r左边删到最小值,右边删到最大值

取三者最小值即可


解法一:递归分治法

完整代码

class Solution {
private:
    struct Result {
        int value;
        int position;
    };//定义结构体用结构体操作
    
    // 递归找最大值及其位置
    Result findMax(vector<int>& nums, int l, int r) {
        if (l == r) return {nums[l], l};
        int mid = (l + r) / 2;
        Result left = findMax(nums, l, mid);
        Result right = findMax(nums, mid + 1, r);
        return (left.value >= right.value) ? left : right;
    }
    
    // 递归找最小值及其位置
    Result findMin(vector<int>& nums, int l, int r) {
        if (l == r) return {nums[l], l};
        int mid = (l + r) / 2;
        Result left = findMin(nums, l, mid);
        Result right = findMin(nums, mid + 1, r);
        return (left.value <= right.value) ? left : right;
    }
    
public:
    int minimumDeletions(vector<int>& nums) {
        int n = nums.size();
        if (n == 1) return 1;
        
        Result maxRes = findMax(nums, 0, n - 1);
        Result minRes = findMin(nums, 0, n - 1);
        
        int l = min(minRes.position, maxRes.position);
        int r = max(minRes.position, maxRes.position);
        
        return min({
            r + 1,              // 只从左删
            n - l,              // 只从右删
            l + 1 + n - r       // 两边同时删
        });
    }
};

算法分析

时间复杂度:O(n log n)

  • 递归树高度为 log n,每层需要合并左右结果

  • 每个递归调用都进行常数次比较操作

  • 实际比较次数:T(n) = 2T(n/2) + 1,解得 T(n) = n - 1

空间复杂度:O(log n)

  • 递归调用栈的深度

  • 每层需要存储左右子问题的 Result 结构

优缺点

优点

  • 展示了经典的分治思想

  • 代码结构清晰,易于理解递归模式

  • 适合用于教学演示分治算法

缺点

  • 实现复杂,参数传递容易出错(原代码中就有参数顺序错误)

  • 递归有栈溢出风险(对于超大数组)

  • 常数因子较大,实际运行速度慢

  • 找最大值和最小值需要两次独立的递归遍历


解法二:哈希表法//先找值再找位置

完整代码

class Solution {
public:
    int minimumDeletions(vector<int>& nums) {
        int n = nums.size();
        if (n == 1) return 1;
        
        // 构建值到位置的映射
        unordered_map<int, int> posMap;
        for (int i = 0; i < n; i++) {
            posMap[nums[i]] = i;
        }
        
        // 找最大值和最小值
        int maxVal = nums[0], minVal = nums[0];
        for (int num : nums) {
            maxVal = max(maxVal, num);
            minVal = min(minVal, num);
        }
        
        int maxIdx = posMap[maxVal];
        int minIdx = posMap[minVal];
        
        int l = min(minIdx, maxIdx);
        int r = max(minIdx, maxIdx);
        
        return min({
            r + 1,
            n - l,
            l + 1 + n - r
        });
    }
};

算法分析

时间复杂度:O(n)

  • 第一次遍历:构建哈希表(O(n))

  • 第二次遍历:找最大最小值(O(n))

  • 总共 O(n)

空间复杂度:O(n)

  • 哈希表存储 n 个元素的位置

优缺点

优点

  • 查找位置是 O(1),非常快速

  • 逻辑清晰,易于实现

  • 适合需要多次查询位置的场景

缺点

  • 空间开销大,需要额外的 O(n) 空间

  • 当数组有重复元素时,后出现的值会覆盖前面的位置(导致错误)

  • 对于简单找最值的问题,使用哈希表过于复杂

  • 需要两次遍历,性能不如一次遍历


解法三:STL算法法(最优解)

完整代码

class Solution {
public:
    int minimumDeletions(vector<int>& nums) {
        int n = nums.size();
        if (n == 1) return 1;
        
        // 直接使用 STL 算法找最值位置
        int minIdx = min_element(nums.begin(), nums.end()) - nums.begin();
        int maxIdx = max_element(nums.begin(), nums.end()) - nums.begin();
        
        int l = min(minIdx, maxIdx);
        int r = max(minIdx, maxIdx);
        
        return min({
            r + 1,              // 只从左删
            n - l,              // 只从右删
            l + 1 + n - r       // 两边同时删
        });
    }
};

算法分析

时间复杂度:O(n)

  • min_element 和 max_element 各遍历一次

  • 实际上可以在一次遍历中同时找到最值位置,但 STL 分开实现

空间复杂度:O(1)

  • 只使用常数个额外变量

优缺点

优点

  • 代码最简洁,只有 5-6 行核心逻辑

  • 使用标准库,可靠性最高

  • 不易出错,易于维护

  • 空间效率最优

缺点

  • 需要遍历两次(可优化为一次)

  • 依赖 STL 实现,对于特殊环境可能不适用


三种解法对比总结

维度解法一:递归分治解法二:哈希表解法三:STL算法
时间复杂度O(n log n)O(n)O(n)
空间复杂度O(log n)O(n)O(1)
代码行数~40 行~25 行~12 行
实现难度较难中等简单
出错概率
适用场景教学演示多次查询工程实践
处理重复元素✅ 安全❌ 会覆盖✅ 安全
递归栈风险⚠️ 存在✅ 无✅ 无

优化建议

如果追求极致性能,可以在一次遍历中同时找到最大值和最小值的位置:

class Solution {
public:
    int minimumDeletions(vector<int>& nums) {
        int n = nums.size();
        if (n == 1) return 1;
        
        int minIdx = 0, maxIdx = 0;
        for (int i = 1; i < n; i++) {
            if (nums[i] < nums[minIdx]) minIdx = i;
            if (nums[i] > nums[maxIdx]) maxIdx = i;
        }
        
        int l = min(minIdx, maxIdx);
        int r = max(minIdx, maxIdx);
        
        return min({r + 1, n - l, l + 1 + n - r});
    }
};

这样只需 O(n) 时间,O(1) 空间,且只遍历一次。

对于这道题:

  1. 解法三(STL算法)最优选择:代码简洁、安全可靠、易于维护

  2. 解法二(哈希表):空间浪费且有重复元素问题

  3. 解法一(递归分治)适合学习:展示了分治思想,但不适合实际应用

在实际工程开发中,优先使用标准库提供的功能,既能提高开发效率,又能减少 bug。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值