Medium
题目描述
给你一个下标从 0 开始的整数数组 nums。
如果存在一些下标 0 <= index1 < index2 < ... < indexk < nums.length,使得 nums[index1] | nums[index2] | ... | nums[indexk] = x,那么我们说整数 x 可以从 nums 中表达出来。换句话说,如果一个整数可以写成 nums 某个子序列的按位或运算结果,那么它就是可表达的。
返回不能从 nums 中表达出来的最小正整数。
示例 1:
输入:nums = [2,1]
输出:4
解释:1 和 2 已经在数组中。我们知道 3 是可表达的,因为 nums[0] | nums[1] = 2 | 1 = 3。由于 4 不可表达,我们返回 4。
示例 2:
输入:nums = [5,3,2]
输出:1
解释:我们可以证明 1 是不可表达的最小数字。
提示:
1 <= nums.length <= 10^51 <= nums[i] <= 10^9
解题思路
解题思路
这道题的关键洞察是:答案一定是 2 的幂次。
为什么?考虑按位或运算的性质:
- 按位或只能将某个位从 0 变为 1,不能从 1 变为 0
- 如果我们想要构造一个数 x,那么 x 的每个为 1 的位都必须能从数组中的某个数获得
- 对于 2 的幂次 2^k,它只有第 k 位为 1,其他位都为 0
因此,如果数组中不存在某个 2 的幂次 2^k,那么无论如何进行按位或运算,都无法得到 2^k。而任何大于 2^k 且包含第 k 位的数,都可以通过 2^k 与其他数的按位或得到。
算法步骤:
- 将数组转换为集合,便于快速查找
- 从 2^0 = 1 开始,依次检查每个 2 的幂次是否在数组中
- 返回第一个不在数组中的 2 的幂次
这种方法的时间复杂度很低,因为即使数组中的最大值是 10^9,我们最多也只需要检查 30 个 2 的幂次(2^30 > 10^9)。
代码实现
class Solution {
public:
int minImpossibleOR(vector<int>& nums) {
unordered_set<int> numSet(nums.begin(), nums.end());
for (int power = 0; power <= 30; power++) {
int powerOf2 = 1 << power;
if (numSet.find(powerOf2) == numSet.end()) {
return powerOf2;
}
}
return 1 << 31;
}
};
class Solution:
def minImpossibleOR(self, nums: List[int]) -> int:
num_set = set(nums)
power_of_2 = 1
while power_of_2 in num_set:
power_of_2 <<= 1
return power_of_2
public class Solution {
public int MinImpossibleOR(int[] nums) {
HashSet<int> numSet = new HashSet<int>(nums);
for (int power = 0; power <= 30; power++) {
int powerOf2 = 1 << power;
if (!numSet.Contains(powerOf2)) {
return powerOf2;
}
}
return 1 << 31;
}
}
var minImpossibleOR = function(nums) {
const numSet = new Set(nums);
let powerOf2 = 1;
while (numSet.has(powerOf2)) {
powerOf2 <<= 1;
}
return powerOf2;
};
复杂度分析
| 复杂度类型 | 分析 |
|---|---|
| 时间复杂度 | O(n + log C),其中 n 是数组长度,C 是数组中的最大值。构建集合需要 O(n),检查 2 的幂次最多需要 O(log C) 次 |
| 空间复杂度 | O(n),用于存储集合 |