【问题标题】:Finding gaps in sequence of numbers查找数字序列中的空白
【发布时间】:2008-12-18 21:38:22
【问题描述】:

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

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

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

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

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

【问题讨论】:

    标签: c++ stl


    【解决方案1】:

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

    【讨论】:

    • 在很多方面这正是我所要求的。我将选择一个稍微不那么像 STL 的版本,因为我现在对无穷无尽的谓词函数有点失望。滚动 C++ lambdas。
    • 这个问题对空向量有一个明确的答案。在一个空向量中,没有出现在向量中的最小整数 >= 1 是 1。在 STL 术语中:我们正在寻找最小的 N>=1 使得 find(begin,end,N)==end。
    【解决方案2】:

    选中的答案使用

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

    【讨论】:

      【解决方案3】:

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

      【讨论】:

        【解决方案4】:

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

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

        【讨论】:

        • 聪明的解决方案
        • 粗略实现:ideone.com/5C1iQ3,稍作修改,始终在位向量中包含 0
        【解决方案5】:

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

        【讨论】:

        • 观察力不错。我想知道是否也可以调整第 k 个最小的算法,或者 nlogn 排序?
        • 不错!它实际上是有用的。
        • 仅当第一个间隙是一个相当小的数字时。在最坏的情况下,[1..size] 中的所有数字都会出现,您的运行时间将是 O(size^2)
        • O() 表示法根据定义是最坏的情况。
        【解决方案6】:

        排序-n-搜索:

        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 一样清晰。

        【讨论】:

        • 好的,非常感谢。我刚刚做了类似的事情,但使用迭代器并处理最终情况。
        • {1,2,3,4} 应该返回 5 而不是 1。此代码在开始时考虑边缘条件,但在结束时不考虑。
        • @Will 和@Greg,已修复。迭代器留给读者作为练习;)
        • 你应该只与最低的比较。你总是知道下一个值应该是什么。请参阅下面的答案。
        【解决方案7】:

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

        1. 如果N
        2. 首先用min_element得到最小的元素,记为emin
        3. 调用 nth_element 获取 N/2 处的元素,称其为 ehalf
        4. 如果 ehalf != emin+N/2 左边有一个间隙,则通过在整个数组上调用 nth_element 递归地应用此方法,但要求元素 N/4。否则,在右边递归请求元素 3*N/4。

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

        【讨论】:

        • 您认为 emin+N/2 将位于索引 N/2 处的假设假定数组已经排序。
        • 嗯,没有。我没想到。我调用了 nth_element 来查找要按排序顺序排列的元素。 nth_element 使用中值查找算法。有 和优化可能:您可以递归一半数组(第 n 个元素分区)。
        • 顺便说一句:如果您只通过递归处理数组的必要一半进行优化,这比 sort-n-search 平均
        • 查看我的答案以获得可能的实现。
        【解决方案8】:

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

        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 insequence op() 中的测试过于复杂。你可以写得更简单更清楚:insequence = insequence &amp;&amp; x == _current;。一般来说,bool1 ? bool2 : bool3 总是可以写成bool1 &amp;&amp; bool2 || bool3(但它并不总是更易读)。
        • +1 用于向 Kristo 展示使用具有状态的函子执行此操作的 STLish 方式...尽管您不应该使用前导下划线来标​​记局部变量,因为这些名称是为编译器保留的,int当前的_;具有相同的影响,并且保证不会发生冲突。
        【解决方案9】:

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-11-12
          • 1970-01-01
          • 2013-03-29
          • 2014-04-18
          • 1970-01-01
          • 1970-01-01
          • 2021-06-27
          • 1970-01-01
          相关资源
          最近更新 更多