✨✨欢迎来到T_X_Parallel的博客!!
🛰️博客主页:T_X_Parallel
🛰️专栏 : C++算法题
🛰️欢迎关注:👍点赞🙌收藏✍️留言
1. 第一题 525.连续数组
题目
给定一个二进制数组 nums
, 找到含有相同数量的 0
和 1
的最长连续子数组,并返回该子数组的长度。
示例 1:
输入: nums = [0,1]
输出: 2
说明: [0, 1] 是具有相同数量 0 和 1 的最长连续子数组。
示例 2:
输入: nums = [0,1,0]
输出: 2
说明: [0, 1] (或 [1, 0]) 是具有相同数量0和1的最长连续子数组。
提示:
1 <= nums.length <= 105
nums[i]
不是0
就是1
题目解析
根据题目给出的条件,已知数组只有0或者1,要求得到的最长连续子数组中的0和1的数量相同,暴力解法就是使用双指针遍历出每个子数组,使用哈希表统计子数组中的0和1元素的个数,在改变左右指针的时候更新哈希表,然后判断0和1数量是否相同,相同就计算长度,与之前更新的结果比较大小然后更新结果。
暴力解法很容易想到,但是暴力遍历的时间复杂度就是O(N²),容易造成时间超时问题
接下来就暴力解法进行一步步优化,得到最优方案。题目是1和0数量相同的最长子数组,不妨我们把0当成-1,致使改变目的,使得只要子数组所有元素之和为0,则符合题目要求,找出最大长度的子数组即可。
例如一个满足要求的子数组为:001011→ -1 -1 1 -1 1 1 即和为0
即使做了这样的思路转换,时间复杂度仍然没有改变,因为并没有优化遍历方式。接下来就是对遍历过程进行优化。我们只遍历一次,并且每次到达一个元素,就加上该元素(0仍然转换为1),即计算每个元素到数组首位置元素之和。遍历一遍之后可以发现会出现相同的和,两个相同和对应的元素中间进行的+1或者-1操作的结果抵消最后为0才会出现相同的和。例如下面的例子
上面的例子中sum出现了两个1,这就说明,下标1到下标6这段子数组元素之和为0,才会使得sum重新变为1,而这段长度即为两个相同sum值所在下标之差。知道了这样的特点,就可以借此来优化遍历过程了,只需遍历一遍,在遍历的过程中更新sum,并记录每个sum以及所对应的下标,只要找到相同sum即满足题目要求的子数组,比较大小更新结果,记录长度最大的子数组。这样我们只需遍历一次即可即时间复杂度为O(N)
在找到相同sum后可以从上面的图中看出并不需要更新第一个某大小的sum值,只需要第一个sum值到最后一个相同的sum值即为最长的符合要求的子数组。而sum为0是一个特殊的位置,因为在sum初始化的时候同样也是0,所以第一个sum为0的下标应该定为-1。
注:记录首次出现的sum值的下标可以使用哈希表(unordered_map)
代码
int findMaxLength(vector<int>& nums) {
unordered_map<int, int> hash;
int sum = 0, ret = 0;
hash[0] = -1;
for (int i = 0; i < nums.size(); i++)
{
if (nums[i] == 0)
sum += -1;
sum += nums[i];
if (hash.count(sum))
ret = max(i - hash[sum], ret);
else
hash[sum] = i;
}
return ret;
}
2.第二题 面试题17.19.消失的两个数字
题目
给定一个数组,包含从 1 到 N 所有的整数,但其中缺了两个数字。你能在 O(N) 时间内只用 O(1) 的空间找到它们吗?
以任意顺序返回这两个数字均可。
示例 1:
输入:[1]
输出:[2,3]
示例 2:
输入:[2,3]
输出:[1,4]
提示:
nums.length <= 30000
题目解析
如果题中没有在 O(N) 时间内只用 O(1) 的空间找到它们
这个要求的话其实很简单,暴力解法很简单而且很快就能完成,第一种暴力解法就是两层for循环,一个一个去找缺失的数字,时间复杂度是O(N²),不满足题目时间复杂度要求。第二种暴力解法就是使用哈希表去记录有的数字,再去遍历哈希表找到没有的数字,空间复杂度为O(N),不满足题目空间复杂度要求。
最优的方法和暴力解法方向不同,所以不能从暴力解法去优化,而是用到一个核心思想——位运算。很多算法题都会考到位运算,只要做过面试题17.04.消失的数字这个题并且使用最优解法就会想到一个异或运算的特性,即两个相同数字异或等于0,不管什么时候异或,比如a和b是两个数字,a^b^a=b
依靠这个异或特性,让完整数字数组和输入数组一起异或,显然最后得到的是题目所要求两个消失的数字的异或。接下来就是想办法去将这两个数字分开。
从上面给出的例子可以看出,最后的异或结果肯定不为0,因为如果为0,那么消失的两个数字就是相同的数字,不满足题目条件,所以异或结果最后不为0,那么表示两个消失的数字的二进制中的其中一位或者多位是不同的,那么我们就可以通过这个不同来将这个两个数分开。而且我们只需使用二进制中第一个不相同的那位即上面操作后的异或结果第一个为1的那位就可以进行分离数字。
要找到异或结果中第一个为1的那位可以使用 x ^ (-x)这个位运算来找。需要注意的是
如果x==INT_MIN,那么使用这个位运算会造成溢出
,所以需要进行判断
使用这个不同的1,去并(&)上完整数组的每个元素,结果为0和1分别被分为两个不同的数组,而这两个消失的数字分别在这两个数组中,从而被分开,。在使用使用相同方法去并上输入数组,同样分为两个数组,然后将并结果为1的数组异或,为0的也异或,相同的数字就会抵消,最后两个数组中剩下的数字就是题目要我们求的两个消失的数字
代码
vector<int> missingTwo(vector<int>& nums) {
int n = nums.size() + 2;
int x = 0, i = 1;
for (auto e : nums)
x ^= e;
for (int i = 1; i <= n; i++)
x ^= i;
int a = 0, b = 0;
x = (x == INT_MIN ? x : x & (-x));//防止数据溢出
for (int i = 1; i <= n; i++)
{
if (x & i)
a ^= i;
else
b ^= i;
}
for (auto e : nums)
{
if (x & e)
a ^= e;
else
b ^= e;
}
return {a, b};
}
专栏:C++算法题
都看到这里了,留下你们的珍贵的👍点赞+⭐收藏+📋评论吧