在向量中找到第一个缺失的元素

时间:2015-01-27 17:19:19

标签: c++ algorithm vector counting missing-data

这个问题has been asked before但我找不到C ++。

如果我有一个向量并且我有一个起始编号,那么std :: algorithm是否为我提供了找到下一个最高缺失数字的方法?

我显然可以在嵌套循环中写这个,我只是不能动摇我重新发明轮子的感觉。

例如,给定:vector foo{13,8,3,6,10,1,7,0};

起始号码0应找到2 起始号码6应找到9 起始编号-2应找到-1

修改

到目前为止,所有解决方案都需要排序。实际上这可能是必需的,但是必须创建临时排序的vector以适应这种情况,因为foo必须保持不变。

4 个答案:

答案 0 :(得分:6)

至少据我所知,没有标准的算法直接实现你所要求的。

如果你想用O(N log N)复杂度这样做,你可以从排序输入开始。然后使用std::upper_bound查找您要求的号码(如果有)的(最后一个实例)。从那里,你会发现一个与前一个不同的数字。从那里,您将扫描集合中连续数字之间的差异大于1。

在实际代码中执行此操作的一种方法是:

#include <iostream>
#include <algorithm>
#include <vector>
#include <numeric>
#include <iterator>

int find_missing(std::vector<int> x, int number) {
    std::sort(x.begin(), x.end());
    auto pos = std::upper_bound(x.begin(), x.end(), number);

    if (*pos - number > 1)
        return number + 1;
    else {
        std::vector<int> diffs;
        std::adjacent_difference(pos, x.end(), std::back_inserter(diffs));
        auto pos2 = std::find_if(diffs.begin() + 1, diffs.end(), [](int x) { return x > 1; });
        return *(pos + (pos2 - diffs.begin() - 1)) + 1;
    }
}

int main() {
    std::vector<int> x{ 13, 8, 3, 6, 10, 1,7, 0};

    std::cout << find_missing(x, 0) << "\n";
    std::cout << find_missing(x, 6) << "\n";
}

这比您通常认为的最佳提供矢量的外观可以/确实保持未排序(并且未经任何修改)。我已经通过创建向量的副本并在find_missing函数内对副本进行排序来完成此操作。因此,原始载体保持不变。缺点是显而易见的:如果矢量很大,复制它可能/将是昂贵的。此外,这最终会为每个查询排序向量,而不是排序一次,然后根据需要执行尽可能多的查询。

答案 1 :(得分:4)

所以我想我会发一个答案。我不知道std :: algorithm中直接完成此任务的任何内容,但结合vector<bool>,您可以在O(2N)中执行此操作。

template <typename T>
T find_missing(const vector<T>& v, T elem){
    vector<bool> range(v.size());
    elem++;

    for_each(v.begin(), v.end(), [&](const T& i){if((i >= elem && i - elem < range.size())range[i - elem] = true;});

    auto result = distance(range.begin(), find(range.begin(), range.end(), false));

    return result + elem;
}

答案 2 :(得分:3)

  • 首先,您需要对矢量进行排序。请使用std::sort

  • std::lower_bound找到与给定元素大于或等于的第一个元素。 (元素必须至少部分订购)

  • 从那里你可以在连续元素的情况下进行迭代。

  • 处理重复:一种方式是我去的方式:迭代时考虑连续和相等的元素。另一种方法是添加向量/范围包含唯一元素的先决条件。我选择前者是因为它避免了擦除元素。

以下是如何消除有序矢量中的重复项:

v.erase(std::unique(v.begin(), v.end()), v.end());

我的实施:

// finds the first missing element in the vector v
// prerequisite: v must be sorted
auto firstMissing(std::vector<int> const &v, int elem) -> int {
  auto low = std::lower_bound(std::begin(v), std::end(v), elem);

  if (low == std::end(v) || *low != elem) {
    return elem;
  }

  while (low + 1 != std::end(v) &&
         (*low == *(low + 1) || *low + 1 == *(low + 1))) {
    ++low;
  }
  return *low + 1;
}

一般化版本:

// finds the first missing element in the range [first, last)
// prerequisite: the range must be sorted
template <class It, class T = decltype(*std::declval<It>())>
auto firstMissing(It first, It last, T elem) -> T {
  auto low = std::lower_bound(first, last, elem);

  if (low == last || *low != elem) {
    return elem;
  }

  while (std::next(low) != last &&
         (*low == *std::next(low) || *low + 1 == *std::next(low))) {
    std::advance(low, 1);
  }
  return *low + 1;
}

测试用例:

int main() {
  auto v = std::vector<int>{13, 8, 3, 6, 10, 1, 7, 7, 7, 0};    
  std::sort(v.begin(), v.end());

  for (auto n : {-2, 0, 5, 6, 20}) {
    cout << n << ": " << firstMissing(v, n) << endl;
  }

  return 0;
}

结果:

-2: -2  
0: 2  
5: 5  
6: 9  
20: 20  

关于排序的说明:根据OP的评论,他正在寻找一种无法修改向量的解决方案。

您必须对矢量进行排序才能获得有效的解决方案。如果无法修改向量,您可以创建副本并对其进行处理。

如果你一心想要不排序,那就有一种强力解决方案(效率非常低 - O(n ^ 2)):

auto max = std::max_element(std::begin(v), std::end(v));
if (elem > *max) {
  return elem;
}
auto i = elem;
while (std::find(std::begin(v), std::end(v), i) != std::end(v)) {
  ++i;
}
return i;

答案 3 :(得分:1)

第一个解决方案:

对矢量进行排序。找到起始编号,然后查看下一个编号。 这将采用O(NlogN),其中N是向量的大小。

第二个解决方案:

如果数字范围很小,例如(0,M)您可以创建大小为M的布尔向量。对于每个初始向量的数量,使该索引的布尔值为true。稍后您可以通过检查布尔向量来查看下一个缺失的数字。这将需要O(N)时间和O(M)辅助存储器。

相关问题