1521. 找到最接近目标值的函数值
大约 3 分钟
---
1521. 找到最接近目标值的函数值
- 标签:位运算、线段树、数组、二分查找
- 难度:困难
题目链接
题目大意
描述:定义函数 (按位与的结果)。给定数组 和目标值 。
要求:返回 的最小可能值。
说明:
- 。
- 。
- 。
示例:
- 示例 1:
输入:arr = [9,12,3,7,15], target = 5
输出:2
解释:所有可能的 [l,r] 数对包括 [[0,0],[1,1],[2,2],[3,3],[4,4],[0,1],[1,2],[2,3],[3,4],[0,2],[1,3],[2,4],[0,3],[1,4],[0,4]], Winston 得到的相应结果为 [9,12,3,7,15,8,0,3,7,0,0,3,0,0,0] 。最接近 5 的值是 7 和 3,所以最小差值为 2 。- 示例 2:
输入:arr = [1000000,1000000,1000000], target = 1
输出:999999
解释:Winston 输入函数的所有可能 [l,r] 数对得到的函数值都为 1000000 ,所以最小差值为 999999 。解题思路
思路 1:位运算 + 集合去重
1. 核心思想
按位与运算的性质:随着参与与运算的元素增多,结果只会不变或变小(二进制位只能从 变 ,不会从 变 )。
关键观察:
- 固定右端点 ,当左端点 从 向左移动时, 的值是单调不增的(准确说是每个二进制位一旦变成 就不会再变回 )。
- 对于固定的 ,所有可能的 的值种数最多 种(因为每个位最多变化一次)。
利用这个性质,我们可以遍历右端点 ,同时维护一个集合 ,表示以 为右端点的所有可能与运算结果。
2. 具体步骤
第 1 步:初始化 为一个大数, 为空集合。
第 2 步:遍历 中的每个元素 :
- 创建新集合 ,初始包含 。
- 对于 中的每个值 ,计算 ,加入 。
- 用 中的每个值更新 。
- 。
第 3 步:返回 。
3. 为什么集合大小不超过 20?
按位与运算每次只能将某些位从 变为 。一个数的二进制位最多 位。每次与运算只会让结果变小或不变。从 开始向左扩展时,每个二进制位最多从 变 一次,因此不同的结果数不超过 个。
4. 举例说明
以 为例:
- ,:,。
- ,:,,。
- ,:,,。
- ,:,。
- ,:,。
最小值为 。
5. 复杂度分析
- 每个元素最多产生 个结果。
- 总时间 。
- 空间 。
思路 1:代码
class Solution:
def closestToTarget(self, arr: List[int], target: int) -> int:
ans = float('inf')
prev = set()
for x in arr:
cur = {x}
for v in prev:
cur.add(v & x)
for val in cur:
ans = min(ans, abs(val - target))
prev = cur
return ans思路 1:复杂度分析
- 时间复杂度:。
- 空间复杂度:。
思路 2:二分查找 + 线段树(理论可行,不推荐)
也可以用线段树维护区间与值,然后对每个左端点二分查找最接近 target 的右端点。但实现较复杂,且 的效率不如集合去重法。