寻找数字序列中的间隙

发布于 2024-07-11 08:11:22 字数 331 浏览 6 评论 0原文

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

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

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

它对性能并不重要,而且列表很短,因此复制列表和排序等方面不存在任何问题。

我并没有真正陷入困境,但我的 STL 技能严重萎缩,我感觉我要做一些不优雅的事情 - 我很想看看其他人想出了什么。

I have a std::vector containing a handful of numbers, which are not in any particular order, and may or may not have gaps between the numbers - for example, I may have { 1,2,3, 6 } or { 2,8,4,6 } or { 1, 9, 5, 2 }, etc.

I'd like a simple way to look at this vector and say 'give me the lowest number >= 1 which does not appear in the vector'. So,

for the three examples above, the answers would be 4, 1 and 3 respectively.

It's not performance critical, and the list is short so there aren't any issues about copying the list and sorting it, for example.

I am not really stuck for a way to do this, but my STL skills are seriously atrophied and I can feel that I'm about to do something inelegant - I would be interested to see what other people came up with.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

手心的温暖 2024-07-18 08:11:22

您正在寻找的标准算法是 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;
}

The standard algorithm you are looking for is std::adjacent_find.

Here is a solution that also uses a lambda to make the predicate clean:

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;
}
冷月断魂刀 2024-07-18 08:11:22

检查的答案使用 < 进行比较。 != 更简单:

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) 所以我不改变原始向量的顺序。

The checked answer uses < for comparison. != is much simpler:

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

I'm not passing a reference to the vector since a) he said time doesn't matter and b) so I don't change the order of the original vector.

风吹雪碎 2024-07-18 08:11:22

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

Sorting the list and then doing a linear search seems the simplest solution. Depending on the expected composition of the lists you could use a less general purpose sorting algorithm, and if you implement the sort yourself you could keep track of data during the sort that could be used to speed up (or eliminate entirely) the search step. I do not think there is any particularly elegant solution to this problem

动次打次papapa 2024-07-18 08:11:22

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

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

You could allocate a bit vector (of the same length as the input vector), initialize it to zero, then mark all indices that occur (note that numbers larger than the length can be ignored). Then, return the first unmarked index (or the length if all indices are marked, which only happens if all indices occur exactly once in the input vector).

This should be asymptotically faster than sort and search. It will use more memory than sorting if you are allowed to destroy the original, but less memory than sorting if you must preserve the original.

執念 2024-07-18 08:11:22

实际上,如果你进行冒泡排序(你知道......他们首先教你然后告诉你永远不要再使用......),你将能够在排序过程的早期发现第一个间隙,所以你可以停在那里。 这应该会给你最快的总体时间。

Actually, if you do a bubble sort (you know... the one that they teach you first and then tell you to never use again...), you will be able to spot the first gap early in the sorting process, so you can stop there. That should give you the fastest overall time.

平安喜乐 2024-07-18 08:11:22

Sort-n-search:

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;

迭代器的使用意图与 中所示的一样明确@joe_mucchiello 的(编辑:更好)答案

Sort-n-search:

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;

Iterators could be used with just as clear intent as showcased in @joe_mucchiello's (ed: better) answer.

鹤仙姿 2024-07-18 08:11:22

好的,这是我的 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。

这应该比预先完全排序要好一些。

OK, here's my 2 cents. Assume you've got a vector of length N.

  1. If N<=2 you can check directly
  2. First, use min_element to get the smallest element, remember it as emin
  3. Call nth_element to get the element at N/2, call it ehalf
  4. If ehalf != emin+N/2 there's a gap to the left, apply this method recursively there by calling nth_element on the whole array but asking for element N/4. Otherwise, recurse on the right asking for element 3*N/4.

This should be slightly better than sorting completely up front.

夜未央樱花落 2024-07-18 08:11:22

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

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());
}

you could go with something like....

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());
}
☆獨立☆ 2024-07-18 08:11:22

Thomas Kammeyer 答案的可能实现

我发现 Thomas 的方法非常聪明且有用 - 因为我们中的一些人在代码中梦想,而我发现实际的实现有点棘手,我想提供一些现成的东西代码。

这里提出的解决方案尽可能通用:

  • 除了迭代器必须满足 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;
}

A possible implementation of Thomas Kammeyer's answer

I found Thomas' approach really smart and useful - since some of us dream in code and I find the actual implementation a bit tricky I wanted to provide some ready-to-use code.

The solution presented here is as generic as possible:

  • No assumption is made on the type of container or range except their iterators must meet the requirements of ValueSwappable and RandomAccessIterator (due to partial sorting with nth_element)
  • Any number type can be used - the required traits are outlined below

Another improvement I think is that a no-gap condition can be checked early: since we have to scan for the minimum anyway we can also scan for the maximum at the same time and then determine whether the number range even contains a gap worth finding.

Last but not least the same recursive approach can be adapted for sorted ranges! If you encode in a template value parameter whether the range is already sorted, you can simply skip the partial sorting plus make determining minimum/maximum elements a no-op.

#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;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文