找出数字序列的差距

时间:2008-12-18 21:38:22

标签: c++ stl

我有一个包含少数数字的std :: vector,这些数字没有任何特定的顺序,数字之间可能有差距,也可能没有差距 - 例如,我可能有{1,2,3,6}或{2,8,4,6}或{1,9,5,2}等

我想要一个简单的方法来查看这个向量并说“给我最小的数字> = 1,出现在向量中”。所以,

对于上面三个例子,答案分别为4,1和3。

这不是性能关键,而且列表很短,所以没有任何关于复制列表和排序的问题,例如。

我并没有真正坚持这样做的方法,但我的STL技能严重萎缩,我觉得我要做一些不优雅的事情 - 我会有兴趣看看其他人想出的。< / p>

9 个答案:

答案 0 :(得分:12)

您要查找的标准算法是 std::adjacent_find

这是一个解决方案,它也使用lambda来使谓词干净:

int first_gap( std::vector<int> vec )
{
  // Handle the special case of an empty vector.  Return 1.
  if( vec.empty() )
    return 1;

  // Sort the vector
  std::sort( vec.begin(), vec.end() );

  // Find the first adjacent pair that differ by more than 1.
  auto i = std::adjacent_find( vec.begin(), vec.end(), [](int l, int r){return l+1<r;} );

  // Handle the special case of no gaps.  Return the last value + 1.
  if ( i == vec.end() )
    --i;

  return 1 + *i;
}

答案 1 :(得分:10)

选中的答案使用&lt;为了比较。 !=更简单:

int find_gap(std::vector<int> vec) {
    std::sort(vec.begin(), vec.end());
    int next = 1;
    for (std::vector<int>::iterator it = vec.begin(); it != vec.end(); ++it) {
        if (*it != next) return next;
       ++next;
    }
    return next;
}

find_gap(1,2,4,5) = 3
find_gap(2) = 1
find_gap(1,2,3) = 4

我没有传递对向量的引用,因为a)他说时间无关紧要b)所以我不改变原始向量的顺序。

答案 2 :(得分:4)

对列表进行排序然后进行线性搜索似乎是最简单的解决方案。根据列表的预期组成,您可以使用较少通用的排序算法,如果您自己实现排序,则可以在排序期间跟踪数据,以便加速(或完全消除)搜索步骤。我不认为这个问题有任何特别优雅的解决方案

答案 3 :(得分:4)

您可以分配一个位向量(与输入向量长度相同),将其初始化为零,然后标记出现的所有索引(请注意,可以忽略大于长度的数字)。然后,返回第一个未标记的索引(如果所有索引都被标记,则返回长度,只有当所有索引在输入向量中只出现一次时才会发生。)

这应该比排序和搜索渐近快。如果允许销毁原始文件,它将使用比排序更多的内存,但如果必须保留原始文件,则会比排序更少的内存。

答案 4 :(得分:2)

排序正搜索:

std::sort(vec.begin(), vec.end());
int lowest = 1;
for(size_t ii = 1; ii < vec.size(); ++ii)
{
    if (vec[ii - 1] + 1 < vec[ii])
    {
        lowest = (vec[ii - 1] + 1);
        break;
    }
}

/* 1, 2, ..., N case */
if (lowest == vec[0]) lowest = (*vec.back()) + 1;

迭代器的使用方式与showcased in @joe_mucchiello's (ed: better) answer一样明确。

答案 5 :(得分:2)

实际上,如果你进行冒泡排序(你知道......他们先教你的那个,然后告诉你再也不用了......),你就能在排序的早期发现第一个缺口过程,所以你可以停在那里。这应该会给你最快的整体时间。

答案 6 :(得分:1)

好的,这是我的2美分。假设你有一个长度为N的向量。

  1. 如果N <= 2,您可以直接查看
  2. 首先,使用min_element获取最小元素,记住它为emin
  3. 调用nth_element将元素设为N / 2,称之为ehalf
  4. 如果ehalf!= emin + N / 2左边有一个间隙,那么通过在整个数组上调用nth_element但是要求元素N / 4来递归地应用这个方法。否则,在右边递归,询问元素3 * N / 4。
  5. 这应该比预先完全排序要好一些。

答案 7 :(得分:1)

你可以选择类似......

struct InSequence
{
    int _current;   bool insequence;
    InSequence() : _current(1), insequence(true){}
    bool operator()(int x) {         
        insequence = insequence ? (x == _current) : false;  
        _current++; 
        return insequence;
    }
};

int first_not_in_sequence(std::vector<int>& v)
{
    std::sort(v.begin(), v.end());
    return 1+std::count_if(v.begin(), v.end(),InSequence());
}

答案 8 :(得分:0)

Thomas Kammeyer答案的可能实施

我发现托马斯的方法非常聪明和有用 - 因为我们中的一些人梦想着代码,我发现实际的实现有点棘手,我想提供一些现成的代码。

此处介绍的解决方案尽可能通用:

  • 不对容器或范围的类型做出任何假设,除非它们的迭代器必须满足ValueSwappable和RandomAccessIterator的要求(由于使用nth_element进行部分排序)
  • 可以使用任何数字类型 - 下面列出了所需的特征

我认为另一个改进是可以提前检查无间隙条件:因为我们必须扫描最小值,我们也可以同时扫描最大值,然后确定数字范围是否包含间隙值得一试。

最后但并非最不重要的是,相同的递归方法可以适用于排序范围!如果您在模板值参数中编码该范围是否已经排序,您可以简单地跳过部分排序加上确定最小/最大元素为无操作。

#include <type_traits>
#include <iterator>
#include <tuple>
#include <utility>
#include <algorithm>
#include <cstddef>

// number type must be:
// * arithmetic
//   * subtractable (a - b)
//   * divisible by 2 (a / 2)
// * incrementable (++a)
// * less-than-comparable (a < b)
// * default-constructible (A{})
// * copy-constructible
// * value-constructible (A(n))
// * unsigned or number range must only contain values >0

/** Find lowest gap value in a range */
template<typename Range>
typename std::remove_reference_t<Range>::value_type
lowest_gap_value_unsorted(Range&& r)
{
    static_assert(!std::is_lvalue_reference_v<Range> && !std::is_const_v<Range>, "lowest_gap_value_unsorted requires a modifiable copy of the passed range");

    return lowest_gap_value_unsorted(std::begin(r), std::end(r), std::size(r));
}

/** Find lowest gap value in a range with specified size */
template<typename Range>
typename std::remove_reference_t<Range>::value_type
lowest_gap_value_unsorted(Range&& r, std::size_t N)
{
    static_assert(!std::is_lvalue_reference_v<Range> && !std::is_const_v<Range>, "lowest_gap_value_unsorted requires a modifiable copy of the passed range");

    return lowest_gap_value_unsorted(std::begin(r), std::end(r), N);
}

/** Find lowest gap value in an iterator range */
template<typename Iterator>
typename std::iterator_traits<Iterator>::value_type
lowest_gap_value_unsorted(Iterator first, Iterator last)
{
    return lowest_gap_value_unsorted(first, last, std::distance(first, last));
}

/** Find lowest gap value in an iterator range with specified size */

template<typename Iterator>
typename std::iterator_traits<Iterator>::value_type
lowest_gap_value(Iterator first, Iterator last, std::size_t N)
{
    typedef typename std::iterator_traits<Iterator>::value_type Number;

    if (bool empty = last == first)
        return increment(Number{});

    Iterator minElem, maxElem;
    std::tie(minElem, maxElem) = std::minmax_element(first, last);

    if (bool contains0 = !(Number{} < *minElem))
        throw std::logic_error("Number range must not contain 0");

    if (bool missing1st = increment(Number{}) < *minElem)
        return increment(Number{});

    if (bool containsNoGap = !(Number(N) < increment(*maxElem - *minElem)))
        return increment(*maxElem);

    return lowest_gap_value_unsorted_recursive(first, last, N, *minElem);
}

template<typename Iterator>
typename std::iterator_traits<Iterator>::value_type
lowest_gap_value_unsorted_recursive(Iterator first, Iterator last, std::size_t N, typename std::iterator_traits<Iterator>::value_type minValue)
{
    typedef typename std::iterator_traits<Iterator>::value_type Number;

    if (N == 1)
        return ++minValue;
    if (N == 2)
    {
        // determine greater of the 2 remaining elements
        Number maxValue = !(minValue < *first) ? *std::next(first) : *first;
        if (bool gap = ++minValue < maxValue)
            return minValue;
        else
            return ++maxValue;
    }

    Iterator medianElem = std::next(first, N / 2);
    // sort partially
    std::nth_element(first, medianElem, last);

    if (bool gapInLowerHalf = (Number(N) / 2 < *medianElem - minValue))
        return lowest_gap_value_unsorted_recursive(first, medianElem, N / 2, minValue);
    else
        return lowest_gap_value_unsorted_recursive(medianElem, last, N / 2 + N % 2, *medianElem);
};

template<typename T>
T increment(T v)
{
    return ++v;
}