《剑指Offer》第二版 -- 持续更新
AcWing 13 找出数组中重复的数字
给定一个长度为n的整数数组 nums,数组中所有的数字都在0∼n−1的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。
注意:如果某些数字不在0∼n−1的范围内,或数组中不包含重复数字,则返回 -1。
数据范围:0 <= n <= 1000
测试样例:
给定 nums = [2, 3, 5, 4, 3, 2, 6, 7]。
返回 2 或 3。
算法思路:
由于题目中的条件“所有的数字都在0~n-1范围内”,因此能够假定如果没有任何重复数字,则数组中的每一个数字都有一个数值相同的下标存在,也就是说:排序之后是一组下标等于数组元素的序列。
设定数组为:a[6] = [3, 5, 1, 2, 4, 0],该数组中没有任何重复数字,排序之后为:a_index[6] = [0, 1, 2, 3, 4, 5],符合a[i] = a_index[i],按照样例中的数组来算,排序之后为:[2, 2, 3, 3, 4, 5, 6, 7]对应下标[0, 1, 2, 3, 4, 5, 6, 7],下标0, 1, 2的部分不符合nums[i] = nums_index[i],里面的重复数字为2, 3,其中3 == 3等价于nums[2] == nums[nums[2]],说明原本应该是3的位置已经有一个3了,但是现在又多了一个,因此得出结论3是多出来的数字,数字2同理分析。
代码实现:
class Solution {
public:
int duplicateInArray(vector<int>& nums) {
int n = nums.size();
for (auto x : nums) {
if (x < 0 || x >= n) return -1;
}
for (int i = 0; i < n; i++) {
while (nums[i] != i) {
if (nums[nums[i]] == nums[i]) {
return nums[i];
}
swap(nums[i], nums[nums[i]]);
}
}
return -1;
}
};
AcWing 14 不修改数组找出重复数字
给定一个长度为n+1的数组nums,数组中所有的数均在1∼n的范围内,其中n≥1。请找出数组中任意一个重复的数,但不能修改输入的数组。
数据范围
1≤n≤1000
样例
给定 nums = [2, 3, 5, 4, 3, 2, 6, 7]。
返回 2 或 3。
算法思路
本题使用二分+抽屉原理实现代码。
抽屉原理:
n + 1个苹果放在n个抽屉里,那么至少有1个抽屉中会放2个苹果。
在本题中,抽屉就是数组长度,苹果就是数组元素,由于长度为n + 1,而数组中元素范围是1~n,因此必有一个数字出现了两次。
将1~n分为两个区间[l, mid]和[mid + 1, r],注意:这里不是分原数组,而是分的数值[1, 2, ..., n],这样分之后由于数组元素有n + 1个并且元素数值有n个,因此一定存在落在其中一个区间的数的个数大于该区间应有的长度。
接着遍历原数组,计算出有多少个数字落在区间[l, mid]中,如果发现落在该区间数字数量s >= mid - l + 1,就说明重复数字一定出现在区间[l, mid]中。
代码实现
class Solution {
public:
int duplicateInArray(vector<int>& nums) {
int l = 1, r = nums.size() - 1; // 长度nums.size()为n + 1
while (l < r) {
int mid = l + r >> 1;
int s = 0;
for (auto num : nums) s += num >= l && num <= mid;
if (s > mid - l + 1) r = mid;
else l = mid + 1;
}
return l;
}
};

浙公网安备 33010602011771号