当前位置: 首页 > news >正文

LeetCode 1300题解析:二分查找优化数组和最接近目标值

1. 问题背景与题目解析

今天我们来拆解LeetCode第1300题——"Sum of Mutated Array Closest to Target"。这是一道中等难度的算法题,主要考察对数组操作和二分查找的应用能力。题目要求我们将给定数组转变为一个特定形式的数组,使得转变后的数组和最接近目标值。

题目具体描述如下: 给定一个整数数组arr和一个目标值target,我们需要找到一个整数value,使得将数组中所有大于value的元素替换为value后,数组的和最接近target。如果有多个value满足条件,我们选择最小的那个。

举个例子: 输入:arr = [4,9,3], target = 10 输出:3 解释:当选择value=3时,数组变为[3,3,3],和为9,与target的差值为1;选择value=4时,数组变为[3,4,4],和为11,差值也是1。我们选择较小的value=3。

2. 解题思路分析

2.1 暴力解法与优化方向

最直观的解法是暴力枚举所有可能的value值,计算对应的数组和,然后找出最接近target的那个value。但是这种方法的时间复杂度是O(n*max(arr)),当数组元素很大时效率极低。

我们需要寻找更高效的解法。观察题目特点:

  1. 当value增加时,数组和单调不减
  2. 我们需要找到使数组和最接近target的value 这些特征提示我们可以使用二分查找来优化搜索过程。

2.2 二分查找的应用

二分查找通常用于在有序序列中快速定位目标值。在本问题中,我们可以将value的可能取值看作一个有序序列(从0到max(arr)),然后通过二分法快速定位最优value。

具体思路:

  1. 确定搜索范围:value的最小可能值是0,最大值是原数组的最大值(因为更大的value不会改变数组和)
  2. 在搜索范围内进行二分查找
  3. 对于每个中间值mid,计算对应的数组和
  4. 根据数组和与target的比较结果调整搜索范围
  5. 记录最接近target的value

3. 详细实现步骤

3.1 预处理与边界情况

首先处理一些边界情况:

  1. 如果数组和已经小于等于target,直接返回数组最大值(因为此时增大value只会使和更大,偏离target)
  2. 如果数组最小值乘以数组长度大于target,返回target/数组长度(因为此时所有元素都需要缩小)
def findBestValue(arr, target): arr.sort() n = len(arr) prefix = [0] for num in arr: prefix.append(prefix[-1] + num) # 边界情况处理 if prefix[-1] <= target: return arr[-1] if arr[0] * n >= target: value = target // n if abs(value * n - target) <= abs((value + 1) * n - target): return value else: return value + 1

3.2 二分查找实现

接下来实现二分查找的核心部分:

left, right = 0, arr[-1] best_value = 0 min_diff = float('inf') while left <= right: mid = (left + right) // 2 # 找到第一个大于mid的元素索引 index = bisect.bisect_right(arr, mid) current_sum = prefix[index] + (n - index) * mid current_diff = abs(current_sum - target) # 更新最优解 if current_diff < min_diff or (current_diff == min_diff and mid < best_value): min_diff = current_diff best_value = mid # 调整搜索范围 if current_sum < target: left = mid + 1 else: right = mid - 1 return best_value

3.3 计算数组和的优化

为了快速计算转变后的数组和,我们使用了前缀和技巧:

  1. 先对数组排序
  2. 计算前缀和数组
  3. 对于给定的value,使用二分查找确定哪些元素需要被替换
  4. 数组和 = 不需要替换的元素和 + 需要替换的元素个数 * value

这种方法将每次计算数组和的时间复杂度从O(n)降低到O(logn),大大提高了整体效率。

4. 复杂度分析与优化验证

4.1 时间复杂度分析

让我们分析算法的时间复杂度:

  1. 排序数组:O(nlogn)
  2. 计算前缀和:O(n)
  3. 二分查找:O(log(max(arr)))
  4. 每次二分查找中的操作:O(logn)(bisect操作) 因此总时间复杂度为O(nlogn + log(max(arr)) * logn)

4.2 空间复杂度分析

空间复杂度主要来自:

  1. 存储排序后的数组:O(n)
  2. 前缀和数组:O(n) 因此总空间复杂度为O(n)

4.3 正确性验证

让我们验证几个测试用例:

  1. 示例1: 输入:[4,9,3], target=10 输出:3 验证:value=3时和为9,差值为1;value=4时和为11,差值也是1。选择较小的3,正确。

  2. 示例2: 输入:[2,3,5], target=10 输出:5 验证:value=5时和为10,正好等于target,正确。

  3. 边界情况: 输入:[1,2,3], target=100 输出:3 验证:数组和已经小于target,返回最大值3,正确。

5. 实际编码中的注意事项

5.1 整数除法的处理

在计算target//n时,需要注意Python的整数除法是向下取整。我们需要比较value和value+1两种情况:

value = target // n if abs(value * n - target) <= abs((value + 1) * n - target): return value else: return value + 1

5.2 等距离情况的处理

当两个不同的value对应的数组和与target的差值相等时,我们需要选择较小的value。这在二分查找的更新步骤中需要特别注意:

if current_diff < min_diff or (current_diff == min_diff and mid < best_value): min_diff = current_diff best_value = mid

5.3 二分查找终止条件

二分查找的终止条件是left > right,但在此之前我们已经记录了最佳解。不需要等到循环结束才返回结果。

6. 算法优化与变种思考

6.1 双指针优化

在已经排序的数组中,我们可以使用双指针技术替代二分查找来定位需要替换的元素,这将进一步优化时间复杂度:

index = 0 while index < n and arr[index] <= mid: index += 1

6.2 浮点数解的可能性

如果允许value为浮点数,我们可以得到更精确的解。但题目要求value必须是整数,因此我们需要在相邻整数中选择更优解。

6.3 多目标优化

考虑扩展问题:如果有多个target需要处理,我们可以预先计算所有可能的value和对应的数组和,然后对每个target进行查询。这种情况下,预处理的时间可能更值得。

7. 完整代码实现

以下是完整的Python解决方案:

import bisect def findBestValue(arr, target): arr.sort() n = len(arr) prefix = [0] for num in arr: prefix.append(prefix[-1] + num) # 边界情况处理 if prefix[-1] <= target: return arr[-1] if arr[0] * n >= target: value = target // n if abs(value * n - target) <= abs((value + 1) * n - target): return value else: return value + 1 left, right = 0, arr[-1] best_value = 0 min_diff = float('inf') while left <= right: mid = (left + right) // 2 index = bisect.bisect_right(arr, mid) current_sum = prefix[index] + (n - index) * mid current_diff = abs(current_sum - target) # 更新最优解 if current_diff < min_diff or (current_diff == min_diff and mid < best_value): min_diff = current_diff best_value = mid # 调整搜索范围 if current_sum < target: left = mid + 1 else: right = mid - 1 return best_value

8. 测试用例设计

为了确保代码的正确性,我们应该设计全面的测试用例:

  1. 常规情况:
assert findBestValue([4,9,3], 10) == 3 assert findBestValue([2,3,5], 10) == 5
  1. 边界情况:
assert findBestValue([1,2,3], 100) == 3 # 数组和小于target assert findBestValue([100,200,300], 50) == 17 # 所有元素都需要缩小
  1. 等距离情况:
assert findBestValue([1,2,3,4,5], 11) == 3 # sum=10和sum=12都差1,选较小的3
  1. 极值情况:
assert findBestValue([], 10) == 0 # 空数组 assert findBestValue([5], 10) == 5 # 单元素数组

9. 实际应用场景

这类问题在实际开发中有多种应用场景:

  1. 资源分配:在有限的资源(target)下,如何公平地限制每个用户的资源使用(value)
  2. 图像处理:像素值归一化时,如何选择截断阈值
  3. 数据压缩:在保持数据总和接近原数据的前提下,如何减少数据值的范围

理解这类问题的解法,有助于我们在面对实际工程问题时,能够快速识别问题模式并应用合适的算法解决。

http://www.jsqmd.com/news/1299459/

相关文章:

  • 2026苏州靠谱财税公司选型:综合对比下的几家服务商 - 资讯报道
  • 【IEEE出版 | EI检索】第十一届机械制造技术与材料工程国际会议(MMTME 2026) - 科研小猫(努力毕业版)
  • 沪上合规黄金回收门店推荐,标准化流程,杜绝回炉费灰色扣费 - 日常比对手册
  • 翻转二叉树:经典面试题解析与实现技巧
  • Python函数def详解:从基础语法到高级应用与避坑指南
  • 告别命令行恐惧:用Zenmap图形化界面玩转Nmap五大核心扫描模式
  • 5秒破解百度网盘提取码:智能工具如何提升10倍资源获取效率
  • Python全栈项目--基于Python的容器编排工具
  • Get cookies.txt LOCALLY:3分钟掌握浏览器Cookie本地导出终极指南
  • 2026 年石家庄实木家具定制、衣柜定制,家装避坑干货整理 - LYL仔仔
  • 2026 年济南历下区无人机维修 无人机培训,本地飞手实测靠谱服务商 - LYL仔仔
  • 电商客服的“隐形亏损”:为什么客户咨询完就走了?
  • 天津黄金回收常见套路汇总,看懂再卖不花冤枉钱 - 日常比对手册
  • 2026年想好去成都配高度数眼镜选哪家了吗?这些攻略别错过 - 企业推荐官
  • 长沙严重肠胃炎保险拒赔:疾病定义争议与条款限缩的法律应对 - 云间寄笔
  • ABAP 7.40+新语法深度解析:从内表操作到字符串处理的现代化革新
  • 解决Cartopy安装失败:从GEOS/Proj依赖到跨平台环境搭建
  • 汇正财经:汽车出口向好,智能化或迎需求拐点
  • TCP拥塞控制算法探测:从原理到实战的完整指南
  • 2026 培根选购终极实测|多维度硬核拆解,综合最优选 EUROFOO - 产品评测官
  • 深入解析大小端、字节对齐与格式化对齐:数据存储与呈现的核心概念
  • [具身智能-696]:为什么称为直流减速电机?减速体现在什么地方?
  • 河池市防水补漏_2026桂北喀斯特山区漏水维修攻略与五大正规团队推荐 - 雨婺虹房屋维修
  • Qoder免费开放三大AI大模型:通义千问3.8 Max、Kimi与Ultra深度评测
  • 2026年,四川那些口碑佳的包车旅行社服务商究竟好在哪? - 企业推荐官
  • 2026年8月郑州诚信靠谱的刑事辩护律师推荐|张国良律师:深耕刑事辩护赛道,以细节与专业化解各类刑案困局 - 十大排行榜推荐
  • 广东镀锌方管厂家直销认准佛山源头,广东阔兴金属有限公司 - 产品评测官
  • STM32F103C8T6 DMA原理与实战:释放CPU性能,实现高效数据搬运
  • 微信投票平台哪个好用?2026海投票vs主流工具实测对比 - 微信投票小程序
  • 口袋门尺寸定制出片品质哪家高,2026十大品牌深度测评,所见即所得不踩雷 - myqiye