💡 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
🔚 结语
这个题目本质是一个经典的贪心 + 局部合并问题,非常适合训练我们对局部最优推动全局最优的理解。通过本题,我们学会了如何在多个选择中找到“最不会破坏后续结构”的操作方式。
如果你喜欢这篇题解,不妨点赞、收藏或分享给更多朋友一起学习~
856

被折叠的 条评论
为什么被折叠?



