题目描述
面试题 17.04. 消失的数字
难度:简单
相关标签:位运算、数组、哈希表、数学、排序
提示
数组nums包含从0到n的所有整数,但其中缺了一个。请编写代码找出那个缺失的整数。你有办法在O(n)时间内完成吗?
注意:本题相对书上原题稍作改动
运行示例
示例 1:
输入:[3,0,1]
输出:2
示例 2:
输入:[9,6,4,2,3,5,7,0,1]
输出:8
题目分析与实现
拿到这个题,我在想:如果给的输入是有序的,再挨个遍历,如果两个相邻元素的差大于1,这不就可以了找到缺失的那个数据了嘛。既然它给出的输入无序,那我就先给它排个序。
方法一:排序
class Solution {
public:
int missingNumber(vector<int>& nums) {
sort(nums.begin(), nums.end());
int n = nums.size();
for(int i = 0; i < n; i++)
{
if(nums[i] != i)
{
return i;
}
}
return n;
}
};
一波操作猛如虎,再看题目——O(n)时间复杂度。“你伤害了我,却一笑而过~~”
喝口咖啡压压惊,再来想想。💡bink->数学方法求解:先通过等差数列求和公式求解0到n的和,在遍历的过程中,将各个元素减掉,剩下的值就是缺失的数字。
方法二:数学
class Solution {
public:
int missingNumber(vector<int>& nums) {
int n = nums.size();
int sum = (1 + n) * n / 2;
for(int i = 0; i < n; i++)
{
sum -= nums[i];
}
return sum;
}
};
这个方法完全是依赖数学计算了。实现得非常完美——O(n)算法时间复杂度 O(1)空间复杂度。但实现完这些后,我们还要想想有没有别得解决方法。
也许会想到哈希表。咱先说说它有什么特点呢?哈希表存储的数据唯一,不能重复,且其时间复杂度极低,为O(n)。那么我们可以这么做:先将输入数组元素全部放入到哈希表中,再对0到n的元素进行查找,如果查找不到,那么它就是缺失的数字。
因为我们只有一个数值,不需要键值和数值对应,因此选择哈希集合会更有效地减少空间。
方法三:哈希表/哈希集合
class Solution {
public:
int missingNumber(vector<int>& nums) {
unordered_set<int>hash;
int n = nums.size();
for(int i = 0; i < n; i++)
{
hash.insert(nums[i]);
}
int missingNum = -1;
for(int i = 0; i <= n; i++)
{
auto ret = hash.find(i);
if(ret == hash.end())
{
missingNum = i;
break;
}
}
return missingNum;
}
};
写完上面的算法,我心身疲惫。但再看看题目标签——“位运算”。这里到底应该如何使用位运算法呢?这个方法应该算是整个题目最难的解法了,但懂得之后就很轻松了。
首先,一起回顾一下异或运算。异或运算时,两个位若相同,则异或结果为0,两个位若不同,则异或结果为1。若两个数相同,则其二进制表示也相同,即各个位均相同。因此,两个相同的数异或结果应为0。
如果我们使用0与1和2进行异或运算,1的二进制表示为0000 0001,2的二进制表示为0000 0010。0与1异或结果为1,再与2异或结果为3。若输入数组为0、1,缺失的数组为2。此时将0、1、2异或操作后的结果与0和1分别异或,会发现结果为2。
若我们将0与0到n的各个数进行异或,再对用户输入的数组的各个元素进行异或,则异或后结果就是缺失的数字。
方法四:位运算
class Solution {
public:
int missingNumber(vector<int>& nums) {
int n = nums.size();
int storage = 0 ^ n;
for(int i = 0; i < n; i++)
{
storage ^= i;
storage ^= nums[i];
}
return storage;
}
};
上面就是今天分享的所有内容啦!下一文章见!
🎇有更优秀的算法,欢迎在评论区讨论!!