剑指 Offer 03. 数组中重复的数字
紫愿__人间尽好,一个致力于大二暑假进厂实习的少年,写作于2022/1/22
题目描述
原始题目:找出数组中重复的数字。
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。
限制条件:2 <= n <= 100000
初步思路
基本思想是类似于hash table,因为数值范围在0 ~ n - 1,那么就可以与数组索引一一对应。
基于以上观点,可得算法:
- 获取最大的可能达到的值max_value = n - 1;
- 为保护原始数组,深拷贝出一份数据_nums
- 若max_value != 0,则将max_value向右移一位,digits++;
- 将1向左移digits位,即可得到一个标志位的值。
- i从0开始,到nums.size()为止:
- 如果_nums[i]去除标志位后的索引指向的向量元素标志位为1,则直接返回_nums[i]标志位置零的值。
- _nums[i]去除标志位后的索引指向的向量元素标志位置为1。
解题代码
int findRepeatNumber(vector<int>& nums) {
unsigned long long max_value = nums.size() - 1;
int digits = 0; // 记录总共有多少位二进制。
int scale = 1; // 比数组最大元素能取到的二进制最高位还高一位的2的倍数。
vector<int> _nums(nums);
while(max_value){
digits++;
max_value >>= 1;
}
scale <<= digits;
max_value = nums.size() - 1;
for (int i = 0; i <= max_value; i++){
if (_nums[_nums[i] & (scale - 1)] >> digits){
return _nums[i] & (scale - 1);
}
_nums[_nums[i] & (scale - 1)] |= scale;
}
return -1;
}
执行成果
优化方案
直接在原始向量上进行修改,找到之后在返回之前,遍历数组,将所有值的标志位都置为0。
代码实现
int findRepeatNumber(vector<int>& nums) {
unsigned long long max_value = nums.size() - 1;
int digits = 0; // 记录总共有多少位二进制。
int scale = 1; // 比数组最大元素能取到的二进制最高位还高一位的2的倍数。
int target; // 将结果保存进来。
while(max_value){
digits++;
max_value >>= 1;
}
scale <<= digits;
max_value = nums.size() - 1;
for (int i = 0; i <= max_value; i++){
if (nums[nums[i] & (scale - 1)] >> digits){
target = nums[i] & (scale - 1);
break;
}
nums[nums[i] & (scale - 1)] |= scale;
}
for (int i = 0; i <= max_value; i++){
nums[i] &= scale - 1;
}
return target;
}
成果截图
可以看到,对比还是蛮大的,这里又重新遍历了一次数组,性能有一定的削减,不过考虑到此算法保证了数组元素的不变性,还是很值得推荐的。
紫愿_人间尽好,在一个致力于大二暑假进厂实习的少年,写作于2022/1/22
此文仅作为学习交流使用