在数组中查找重复项
Find duplicate in an array
给定一个介于 1 和 n 之间的 n + 1 个整数的只读数组,找到一个使用小于 O(n) 的空间并在流中按 O(1) 次顺序遍历流的线性时间重复的数字。
Sample Input: [3 4 1 4 1]
Sample Output : 1/4(any one of these)
如果有多个可能的答案(如上面的示例案例),请输出任何一个。
如果没有重复项,则输出 -1。
我尝试对此进行解决方案,即:
int Solution::repeatedNumber(const vector<int> &A) {
vector<bool> v(A.size(), true);
for (int i = 0; i < A.size(); i++) {
if (v[A[i]])
v[A[i]] = false;
else
return A[i];
}
}
这正在被接受,但这在内存中如何小于 O(n)?
你想知道为什么会接受这是正确的。这个答案是明显的O(n)空间复杂性。您分配了一些与 n 成正比增长的数据量,使其成为 O(n) 空间。无论什么判断你的程序都是错误地接受它。法官可能会接受您的分数,因为您使用的字节数少于 A 分配的字节数,但这只是猜测。
编辑:下面的代码实际上并不是问题的解决方案。它是按照上述思路解决更简单问题的方法。下面的解决方案忽略了流必须是只读的约束。经过一些研究,这个问题似乎是一系列类似问题的非常困难的版本,这些问题类型为"给定 1 到 n 之间的数字范围,找到重复/缺失的数字"。如果只有一个数字重复,并且只有一个 O(n) 时间要求,则可以使用上述布尔向量。如果只有一个数字重复,但你被限制在常量空间,你可以实现这个解决方案,我们使用高斯公式找到从 1 到 n 的整数之和,然后从数组的总和中减去它。如果数组有两个缺失的数字,并且您被限制为常量时间,则可以实现此解决方案,其中我们使用数组的总和和乘积来创建一组方程组,该方程组可以在O(n)时间内用O(1)空间求解。
为了解决上面提出的问题,看起来必须按照这个怪物的顺序实施一些东西。
以下是在其约束范围内解决此问题的方法:
你可以做这样的事情:
#include<vector>
#include<iostream>
int repeating(std::vector<int>& arr)
{
for (int i = 0; i < arr.size(); i++)
{
if (arr[abs(arr[i])] >= 0)
arr[abs(arr[i])] = -arr[abs(arr[i])];
else {
return abs(arr[i]);
}
}
}
int main()
{
std::vector<int> v{1,2,3,4,5,1};
std::cout<<repeating(v)<<std::endl;
std::cout<<sizeof(v)*sizeof(v[0])<<std::endl;
return 0;
}
上面的程序使用输入数组本身来跟踪重复项。对于每个索引 i,数组计算 arr[i]。数组将 arr(arr[i]) 设置为负数。否定值是一种易于逆转的操作(只需获取元素的绝对值),因此它可用于标记数组的索引,而不会破坏数据的完整性。如果你遇到过 arr[abs(arr[i])] 为负数的索引,你就知道你之前在数组中见过 abs(arr[i]))。这使用 O(1) 空间复杂度,遍历数组一次,并且可以修改以返回任何或所有重复的数字。
>std::vector<bool>
是一个位集,所以它将使用n位。在 Big-O 表示法中,O(n/8)=O(n),这意味着空间不小于 O(n)。
我假设他们不查看实际程序,而只在某些示例运行中测量其空间消耗。因此,使用位向量会诱使它相信它比 O(n) 更好。
但我同意你的看法。它不应该被接受。
我有一个解决方案,它需要 O(sqrt(N)) 空间和 O(N) 时间,并遍历列表两次——假设可以在 O(1) 时间内计算整数平方根(对于任意大 N,这可能至少是一个 O(log(N)) 操作)。
- 首先分配一个大小为 ceil(sqrt(N)) 的整数数组
A1
,填充为 0。 - 针对每个元素循环访问数组
x
- 计算
k=floor(sqrt(x))
- 递增
A1[k]
- 如果
A1[k]>2k+1
,则在k²
和(k+1)²-1
之间必须至少有一个重复项。(对于k=floor(sqrt(N))
阈值为N-k²). Remember
k' 并中断第一次迭代
- 计算
- (可选)删除第一个数组
- 分配一个大小
A2
2k+1
填充false
的布尔数组。 - 再次遍历所有
x
:- 检查是否设置了
A2[x-k²]
,如果是,x
是重复的 - 否则,递增
A2[x-k²]
- 检查是否设置了
该解决方案也应该适用于更大和更小的数组(不需要正好是 N+1),如果没有重复项,第一次迭代将运行到最后。两个临时数组都是 O(k)(如果你是迂腐的,第一个是 O(k*log(k)),因为它必须存储最大大小为 sqrt(N) 的整数)。
std::vector<bool>
不像任何其他向量。
std::vector<bool>
是针对bool
型std::vector
的可能节省空间的专业化。
这就是为什么它可能占用更少的内存,因为它可能用一个字节表示多个布尔值,就像位集一样。
@jayson Boubin 在上述答案中提出的解决方案是O(1)-空间方法,它 很好(顺便说一下,它很棒!! ),当允许更改原始数组或意味着更改无关紧要时。但是如果不允许更改原始数组,那么众所周知的解决方案是 O(sqrt(n))-空间和 O(n)-time,这种方法基本上建议我们应该首先考虑sqrt(n)-范围,而第 i 个范围将是 [sqrt(n)*i 到 sqrt(n)*(i+1)],之后我们遍历数组并计数 no。元素位于每个范围中,依此类推...
看看它: Leetcode:查找重复号码
嗯,它在内存中是常量(O(1)),因为你只是在原地进行比较,而不是创建一个新的数据结构来容纳任何东西或任何比较。
您也可以使用像unordered_set这样的哈希表,但这将使用 O(N) 内存 - 但保持 O(N) 时间复杂度。
顺便说一下,我不完全确定这是否是一个"可接受的"解决方案(您发布的内容,因为这是创建一个大小向量 (sizeofA) - 但只是根据您的需求提供解决方案。
- 查找数组中第一个最小值和最后一个最大值元素之间的算术平均值
- 需要使用模板查找数组的第二个最小和最小值
- 用于查找数组中最大元素的出现次数的代码,给出分段错误
- 查找数组中指示性较大但数组中值较小的元素
- C++ 函数,用于查找数组中四个最小最大元素的总和不起作用
- 查找数组中的最小数字
- 查找数组中重复(重复)数字的索引
- 使用一个内存集数组和单个堆栈在 O(n) 中查找数组的下一个更大元素
- 查找数组中给定数字的出现次数
- 使用 std::累加来查找数组的总和
- 代码以查找数组中的最大数字,但它仅将第一个数字显示为最大数字. 有人请告诉我为什么
- 在不进行排序的情况下查找数组中n个最小值的索引
- 当数组在C++中用作参数时,查找数组的元素数
- 查找数组中数组元素的重复出现?
- 查找数组最后 3 个数字的平均值
- 查找数组中是否存在C++元素
- 查找数组中的最小更改数以进行 k 算术级数
- 查找数组的第一个和最后一个索引,其中 from 和 to 元素的顺序总和最大
- 有没有办法在递归中使用指针来查找数组的最小值?
- 在编写代码以查找数组中的对数时获取分段错误