【问题标题】:How to find the minimal missing integer in a list in an STL way如何以 STL 方式查找列表中的最小缺失整数
【发布时间】:2015-10-25 12:09:11
【问题描述】:

我想在给定列表中找到最小的缺失正整数。也就是说,如果给定一个正整数列表,即大于 0 且重复的整数,如何从那些缺少最小的那个中找到。

序列中总是至少缺少一个元素。

例如给定

std::vector<int> S={9,2,1,10};

答案应该是 3,因为缺少的整数是 3,4,5,6,7,8,11,...,最小值是 3。

我想出了这个:

int min_missing( std::vector<int> & S)
{
  int max = std::max_element(S.begin(), S.end());
  int min = std::min_element(S.begin(), S.end());
  int i = min;
  for(; i!=max and std::find(S.begin(), S.end(), i) != S.end() ; ++i);
  return i;
}

这是 O(nmlogn) 的时间,但我不知道是否有更有效的 C++ STL 方法来做到这一点?

这不是一个练习,但我正在做一组自我提升的问题,我发现这是一个非常有趣的问题。我有兴趣看看如何改进这一点。

【问题讨论】:

  • 你能举个例子吗?我不确定我是否理解您的意思。
  • 当您说“高效”时,您是指程序员工作时间方面的时间效率、空间效率还是效率?
  • @ChristianHackl ,除非另有说明,否则通常暗示节省时间。在这种情况下,通常不考虑程序员的工作时间。
  • 这是一个序列 {1,2,...} 暗示它是有序的吗?

标签: c++ algorithm stl


【解决方案1】:

您可以使用std::sort,然后使用带有自定义谓词的std::adjacent_find

int f(std::vector<int>  v)
{
    std::sort(v.begin(), v.end());
    auto i = std::adjacent_find( v.begin(), v.end(), [](int x, int y) 
    {
        return y != x+1;
    } );

    if (i != v.end())
    {
         return *i + 1;
    }
}

当不存在这样的元素时会发生什么是开放的,例如当向量为空时。

【讨论】:

  • 您的解决方案的复杂性是多少?
  • @jjknuth O(n log n):std::sort 以 O(n log n) 为主,std::adjacent_find 是线性的。
【解决方案2】:

找到第一个缺失的正数,时间为 O(n),空间恒定

基本上,当您读取值 a 时,只需与 S[a] 交换,就像 2 应该与 A[2] 交换一样

class Solution {
public:
/**    
 * @param A: a vector of integers
 * @return: an integer
 */
int firstMissingPositive(vector<int> A) {
    // write your code here
    int n = A.size();
    for(int i=0;i<n;)
    {
        if(A[i]==i+1)
            i++;
        else
        {
            if(A[i]>=1&&A[i]<=n&& A[A[i]-1]!=A[i])
                swap(A[i],A[A[i]-1]);
            else
                i++;
        }
    }
    for(int i=0;i<n;i++)
        if(A[i]!=i+1)
            return i+1;
    return n+1;
   }
};

【讨论】:

    【解决方案3】:

    假设数据先排序:

        auto missing_data = std::mismatch(S.cbegin(), S.cend()-1, S.cbegin() + 1,
                                          [](int x, int y) { return (x+1) == y;});
    

    编辑 由于您的输入数据未排序,因此最简单的解决方案是先对它们进行排序:

       std::vector<int> data(S.size());
       std::partial_sort_copy (S.cbegin(), S.cend(), data.begin(), data.end());
       auto missing_data = std::mismatch (data.cbegin(), data.cend()-1, data.cbegin()+1,
                                          [](int x, int y) { return (x+1) == y;});
    

    【讨论】:

      【解决方案4】:

      您可以使用标准模板库 c ++ 的算法来处理您的代码。

      #include <algorithm>    // std::sort
      

      算法中的这个std::sort

      std::vector<int> v={9,2,5,1,3};
      
      std::sort(v.begin(),v.end());
      
      std::cout << v[0];
      

      我希望我明白你在看什么。

      【讨论】:

      • 这仅返回当前数字的最小值。问题是关于那些不存在的。
      【解决方案5】:

      您可以通过构建一组整数并添加在集合中看到的较大的整数,并将未看到的最小值作为计数器来做到这一点。一旦有一个数字等于后者,遍历集合删除元素,直到缺少一个整数。

      具体实施请见下文。

      template<typename I> typename I::value_type solver(I b, I e)
      {
          constexpr typename I::value_type maxseen=
            std::numeric_limits<typename I::value_type>::max();
          std::set<typename I::value_type> seen{maxseen};
      
          typename I::value_type minnotseen(1);
      
          for(I p=b; p!=e;++p)
          {
              if(*p == minnotseen)
              {
                  while(++minnotseen == *seen.begin())
                  {
                      seen.erase(seen.begin());
                  }
      
              } else if( *p > minnotseen)
              {
                  seen.insert(*p);
              }
          }
      
          return minnotseen;
      }
      

      如果你的序列在一个向量中,你应该使用它:

      solver(sequence.begin(),sequence.end());
      

      该算法在时间上为 O(N),在空间上为 O(1),因为它只使用一个计数器、固定大小的附加空间和几个迭代器来跟踪最小值。

      复杂性(增长率顺序)该算法仅保留输入的子集,该子集预计相对于输入的增长率具有恒定的增长顺序,因此在空间中为 O(1)。迭代的增长率为 O(N+NlogK),其中 K 是所见较大数字的较大子序列的增长率。后者是上述恒定增长率的子序列,即 K=1 ,这导致算法具有 O(N) 复杂度。(参见 cmets)

      【讨论】:

      • 这个算法实际上在时间上是 O(n log n),在空间上是 O(n)。 (你忘了你的std::set!)
      • @ruakh 该集合仅保留 n 大小输入(较大的数字)的子集,因此时间上为 O(n+nlogk),空间上为 O(logk)。我假设 k (较大子序列的大小)相对较小,平均而言应该是预期的,因为不应假设无限序列中具有无限长度的重复子序列的数量是无限的(它的增长率较小)。如您所见,分析有点复杂。不过,为了让您放心,我已经对此进行了测试。
      • @ruakh (n) / n! --> 0 与 n->inf 一样快。这解决了这个问题。无论如何,感谢您无缘无故地投反对票。
      • @ruakh ,输入大小不仅是唯一的数字,因为 OP 没有指定,因此对于输入大小为 n 的输入 size(n) 存在无限大小的重复。例如{ 1,1,1,4,4,5,5,1,1,1} 的最小值为 not seen 2 ,关联集的基数为 4 ,输入大小为 10。因此作为 n->inf 最坏情况是 O(n) 和 O(1) 。
      • @ruakh 我认为哈希映射等就是这种情况,声称的绑定摊销成本是恒定的。在一种情况下,您总是会遇到 O(n) 冲突。这里不会发生这种情况,因为随着(输入大小)的增加,倍数最终会超过(内部存储大小比率)/(输入大小)。
      猜你喜欢
      • 2019-04-27
      • 2014-01-31
      • 1970-01-01
      • 1970-01-01
      • 2015-01-08
      • 2021-06-12
      • 1970-01-01
      • 2022-11-23
      • 2021-12-02
      相关资源
      最近更新 更多