本篇博客的知识体系来自labuladong的汇总,对其精妙的方法极为惊叹,特此记录一下,如需查看原文请点击labuladong。
题目:给定一个包含0,1,2,3…,n中的n个序列,找出0…n中没有出现在序列中的那个数。
方法一:排序 O(nlogn)+O(1)
采用排序的方法,然后遍历一遍找到缺失的数字。该方法的时间复杂度为O(n),空间复杂度为O(1)。
方法二:数组方法 O(n)+O(n)
另外开辟数组,如果这个数组出现过,标记一下,找到没有标记的数字。该方法的时间复杂度O(n),空间复杂度为O(n),典型的空间换时间的复杂度。
方法三:位运算 O(n)+O(1)
在我们学习异或运算,相同数字的两个数字异或为0,那么我们异或[0, n]的数据,再异或需要求出[0, n]缺失数据的数组,异或的过程中会出现两个一样的数据相互抵消,最终只剩下缺失值。
代码源自 labuladong
int missingNumber(int[] nums) {
int n = nums.length;
int res = 0;
// 先和新补的索引异或一下
res ^= n;
// 和其他的元素、索引做异或
for (int i = 0; i < n; i++)
res ^= i ^ nums[i];
return res;
}
方法四:等差数列 O(n)+O(1)
这个方法很容易忽略,我们利用等差公式求出等差数列前n项和,然后减去给出缺失值的数组中的数据和,就是我们要的缺失值。但是该方法涉及乘法可能导致数据溢出,所以我们就需要优化该方法。
代码源自labuladong
int missingNumber(int[] nums) {
int n = nums.length;
// 公式:(首项 + 末项) * 项数 / 2
int expect = (0 + n) * (n + 1) / 2;
int sum = 0;
for (int x : nums)
sum += x;
return expect - sum;
}
优化:
我们在遍历的时候一边求和一边减算,具体实现方法请看代码。
代码源自labuladong
public int missingNumber(int[] nums) {
int n = nums.length;
int res = 0;
// 新补的索引
res += n - 0;
// 剩下索引和元素的差加起来
for (int i = 0; i < n; i++)
res += i - nums[i];
return res;
}