在数组中查找重复项

时间:2017-06-19 18:38:47

标签: c++ arrays vector

给定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)小?

5 个答案:

答案 0 :(得分:3)

你想知道为什么会被接受是正确的。这个答案显然是O(n)空间复杂性。您分配了一些与n成比例增长的数据,使其成为O(n)空间。无论判断你的程序是错误的接受它。法官可能会接受你的分数,因为你使用的字节数少于A分配的字节数,但这只是猜测。

  编辑:以下代码实际上并不是解决问题的方法。它是对上述问题的一个简单问题的解决方案。下面的解决方案忽略了必须只读取流的约束。经过一些研究后,似乎这个问题是一系列类似问题的非常困难的版本“给定1到n之间的数字范围,找到重复/缺失的数字”。如果只重复一个数字,并且只有O(n)时间要求,则可以使用上面的bool向量。如果只有一个数字重复,但你被限制在恒定空间,你可以实现这个solution,我们使用高斯公式来找到从1到n的整数之和,并从数组的总和中减去它。如果数组有两个缺失的数字,并且你被限制为恒定时间,你可以实现这个solution我们使用数组的和和乘积来创建一个方程组,可以用O(n)求解与O(1)空间的时间。

     

要解决上面提出的问题,看起来必须按照monstrosity的顺序执行某些操作。

这是一个解决这个问题的解决方案:

你可以这样做:

#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)空间复杂度,遍历数组一次,并且可以修改以返回任何或所有重复的数字。

答案 1 :(得分:3)

std::vector<bool>是一个bitset,所以它将使用n位。在Big-O表示法中,O(n / 8)= O(n),这意味着空间不小于O(n)。

我认为他们不会查看实际程序,而只是在某些示例运行中测量其空间消耗。因此,使用位向量可以让它相信它比O(n)更好。

但我同意你的看法。它不应被接受。

答案 2 :(得分:1)

我有一个需要O(sqrt(N))空间和O(N)时间的解决方案,并且遍历列表两次 - 假设可以在O(1)时间内计算整数平方根(对于任意时间)大N,这可能至少是O(log(N))操作。

  • 首先分配一个大小为ceil(sqrt(N))的整数数组A1,填充为0。
  • 遍历您的数组,为每个元素x
    • compute k=floor(sqrt(x))
    • 增量A1[k]
    • 如果A1[k]>2k+1(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)的整数。)

答案 3 :(得分:0)

std::vector<bool>与其他向量不同。

std::vector<bool>std::vector类型bool的节省空间的专业化。

这就是为什么它可能占用更少的内存的原因,因为它可能代表一个字节(如位集)表示多个布尔值。

答案 4 :(得分:-1)

它在内存中是常量(O(1)),因为你只是简单地进行比较,而不是创建一个新的数据结构来容纳任何东西或进行任何比较。

您也可以使用像unordered_set这样的哈希表,但是它会使用O(N)内存 - 但仍然是O(N)时间复杂度。

我不完全确定这是否是一个“接受”的解决方案(你发布了什么,因为这是创建一个大小的矢量(sizeofA) - 但只是根据你的需要提供解决方案。