一、引言
这是一道非常简单的题目,我们做这道题的目的只有一个:尽量用更多的方法做出这道题:)
先看看题目吧:
Given an array containing n distinct numbers taken form
0, 1, 2, ..., n
, find the one that is missing from the array.For example.
Given nums =[0, 1, 3]
return2
.Note:
Your algorithm should run in linear runtime complexity. Could you implement it using only constant extra space complexity?
简单翻译下:
给定一个含有 n 个唯一的数字的数组,其中数字从
0, 1, 2,.., n
中取,请找到其中消失的那个数字。注意:
你的算法应该是线性时间复杂度。你能在常数空间复杂度下完成这道题吗?
让我们抛开这道题的限制,我们来看看,我们一共能够想出多少个解法?
二、遍历:返回消失的数字
首先,我想到了这么一个思路:
首先排序,然后遍历这个数组,将当前位置应该显示的数字与当前数组中对应位置数字比较,如果不同则返回这个应该显示的数字
代码如下:
// my solution 1 , runtime = 39 ms
class Solution1 {
public:
int missingNumber(vector<int>& nums) {
sort(nums.begin(), nums.end());
for (int i = 0; i < nums.size(); ++i) {
if (i != nums[i]) return i;
}
return nums[nums.size() - 1] + 1;
}
};
这个方法需要注意的是,万一消失的数字是最后一个数字 n ,那么需要手动返回(因为最后一个数字 n 在数组中无法被遍历到)。
这个方法使用了 std::sort
,并且多了一次遍历,时间复杂度不高,并且使用了与数组同长度的空间复杂度。
三、数学公式:n(n + 1)/2
我们还可以使用数学公式,可以说这个方法是非常妙的:
计算出从 0 到 n 的和,然后减去当前数组的和即可
代码如下:
// my solution 2 , runtime = 29 ms
// n ( n + 1) / 2
class Solution2 {
public:
int missingNumber(vector<int>& nums) {
int total = 0;
for (auto i : nums) total += i;
return nums.size() * (nums.size() + 1) / 2 - total;
}
};
这个方法,只有一次遍历,因此是 O(n) 的线性时间复杂度,空间复杂度只有 O(1),可以说是完美达标。
四、BitManipulation:位操作
如果对于位操作熟悉的话,你可能会想到使用异或操作:
遍历数组,将所有元素异或,同时将当前位置应该显示的数字异或,最后的结果即为那个消失的数字
代码如下:
// my solution 4 , runtime = 26 ms
class Solution {
public:
int missingNumber(vector<int>& nums) {
int result = 0;
for (int i = 0; i < nums.size(); ++i) {
result ^= nums[i];
result ^= i;
}
return result ^= nums.size();
}
};
这个方法同样需要注意,在对数组的遍历过程中,遍历不到数字 n,因此最后需要再异或一个数字 n(即 nums.size())。
同样的,这个方法的时间复杂度为 O(n),空间复杂度为 O(1),可以说是完美切合题意的方法。
五、总结
这是一道比较有趣的题目,在使用多个方法解题的过程中,我们使用了不同的思路,这也是一种锤炼。
最后,以许嵩的《通关》这首歌的歌词结尾:
我感谢自己平凡
敢爱敢恨没负担
湖面穿行的谬赞和妄断
不过是通关之夜的辗转
共勉!