3507. 移除最小数对使数组有序 I

💡 LeetCode题解:将数组变为非递减数组的最少合并次数

📝 题目描述

给你一个整数数组 nums,你可以执行如下操作任意次:

  • 选择一个相邻元素对nums[i], nums[i+1]),将它们替换成它们的,即:nums[i] = nums[i] + nums[i+1],并删除 nums[i+1]

你的目标是通过最少的操作次数,把这个数组变成一个非递减数组。一个数组是非递减的,指的是:对于任意 i,都有 nums[i] <= nums[i+1]

返回 最少的操作次数


🎯 示例说明

示例 1:

输入:

nums = [5, 3, 6, 2]

输出:

2

操作过程可能如下:

  • 第一次选择 [5, 3],合并为 8[8, 6, 2]
  • 第二次选择 [8, 6],合并为 14[14, 2]
  • 第三次选择 [14, 2],合并为 16[16] (最终非递减)

但实际上只要进行2次操作就可以得到非递减数组,例如 [5+3=8], 6+2=8,得到 [8, 8]


🧠 解题思路分析

我们要通过合并操作使得数组成为非递减数组。每次操作必须选择相邻一对,因此需要一个策略来决定:

  • 合并哪一对?
  • 合并后怎么判断是否朝着目标前进了?
  • 如何最小化操作次数?

显然,随意合并会导致产生大的数,反而更难满足非递减。因此,贪心策略:每次合并“相邻和最小的一对” 是一种直觉合理的做法,避免过早产生大值。


🚀 解法一:模拟+辅助函数(结构清晰版)

这是结构更清晰、模块化的版本:

class Solution:
    def minimumPairRemoval(self, nums: List[int]) -> int:
        def is_ok(nums):
            for i in range(1, len(nums)):
                if nums[i] < nums[i - 1]:
                    return False
            return True

        def restract(nums, i):
            s = nums[i] + nums[i + 1]
            if len(nums) == 2:
                return [s]
            if i == 0:
                return [s] + nums[2:]
            if i == len(nums) - 2:
                return nums[:-2] + [s]
            return nums[:i] + [s] + nums[i + 2:]

        cnt = 0
        while not is_ok(nums):
            min_index = 0
            min_value = nums[0] + nums[1]
            for i in range(1, len(nums) - 1):
                if nums[i] + nums[i + 1] < min_value:
                    min_value = nums[i] + nums[i + 1]
                    min_index = i
            nums = restract(nums, min_index)
            cnt += 1
        return cnt

✅ 优点:

  • 清晰地分离判断、重构数组逻辑。
  • 容易调试、可扩展。

❌ 缺点:

  • 创建新数组,性能略逊。
  • 使用多个辅助函数,代码稍显冗长。

🚀 解法二:原地操作 + 简洁结构(推荐)

这是一种更加精简、效率更高的实现方式:

class Solution:
    def minimumPairRemoval(self, nums: List[int]) -> int:
        operations = 0
        while True:
            non_decreasing = True
            min_sum = float('inf')
            min_index = -1

            for i in range(len(nums) - 1):
                if nums[i] > nums[i + 1]:
                    non_decreasing = False
                pair_sum = nums[i] + nums[i + 1]
                if pair_sum < min_sum:
                    min_sum = pair_sum
                    min_index = i

            if non_decreasing:
                break

            nums[min_index] = min_sum
            del nums[min_index + 1]
            operations += 1

        return operations

✅ 优点:

  • 更节省内存(原地操作数组)。
  • 遍历过程中同时判断是否非递减 + 找最小合并对。
  • 高效简洁,可读性强。

❌ 缺点:

  • 不适合一步一步调试状态(需要自行加入打印语句)。

📊 方法对比总结

解法

是否原地操作

可读性

执行效率

代码复杂度

解法一(函数版)

中等

解法二(推荐)

✅ 是

✅ 高

✅ 简洁


🧪 补充测试样例

s = Solution()
print(s.minimumPairRemoval([1, 2, 3]))        # 输出 0
print(s.minimumPairRemoval([3, 2, 1]))        # 输出 2
print(s.minimumPairRemoval([5, 3, 6, 2]))     # 输出 2
print(s.minimumPairRemoval([10, 1, 1, 1, 50]))# 输出 2

🔚 结语

这个题目本质是一个经典的贪心 + 局部合并问题,非常适合训练我们对局部最优推动全局最优的理解。通过本题,我们学会了如何在多个选择中找到“最不会破坏后续结构”的操作方式。


如果你喜欢这篇题解,不妨点赞、收藏或分享给更多朋友一起学习~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值