在向量中找到最长的“连续数字”条纹的最快方法是什么?

时间:2012-07-25 15:00:25

标签: c++ algorithm vector find

我有一个已排序的std::vector<int>,我想在此向量中找到最长的“连续数字条纹”,然后返回它的长度和条纹中的最小数字。

为您呈现它: 假设我们有: 1 3 4 5 6 8 9

我希望它返回:maxStreakLength = 4streakBase = 3

可能会出现2条条纹,我们必须选择哪条条纹更长。

最好(最快)的方法是什么?我试图实现这个,但我在向量中处理多个条纹时遇到问题。我应该使用临时向量然后比较它们的长度吗?

5 个答案:

答案 0 :(得分:7)

不能在一次通过向量中执行此操作,并且只存储到目前为止找到的最长起点和长度。您还需要比“N”更少的比较。 *

提示:如果你已经说过一个4长的比赛结束于第5个位置(= 6)以及你必须在下一个位置进行检查?

[*]留给读者练习,找出可能的O()复杂度; - )

答案 1 :(得分:3)

有趣的是,可以通过某种方式利用数组排序的事实来改进算法。首先想到的是:如果您知道输入数组中的所有数字都是唯一的,那么对于数组中的一系列元素[i, j],您可以立即判断该范围内的元素是连续的还是不连续的,而不实际查看范围。如果这种关系成立

array[j] - array[i]  ==  j - i

然后您可以立即说该范围内的元素是连续的。显然,这个标准使用的事实是数组已经排序,数字不会重复。

现在,我们只需要开发一种能够利用该标准的算法。这是一种可能的递归方法:

  1. 递归步骤的输入是元素[i, j]的范围。最初它是[0, n-1] - 整个数组。
  2. 将上述标准应用于范围[i, j]。如果范围是连续的,则无需进一步细分。将范围发送到输出(有关详细信息,请参见下文)。
  3. 否则(如果范围不连续),请将其分为两个相等的部分[i, m][m+1, j]
  4. 递归调用下部([i, m])然后上部([m+1, j])的算法。
  5. 上述算法将使用左第一种方法执行数组的二进制分区和分区树的递归下降。这意味着该算法将以从左到右的顺序找到具有连续元素的相邻子范围。您需要做的就是将相邻的子范围连接在一起。当您收到在步骤2中“发送到输出”的子范围[i, j]时,您必须将其与先前接收的子范围连接,如果它们确实是连续的。或者你必须开始一个新的范围,如果他们不连续。你一直跟踪到目前为止发现的“最长连续范围”。

    就是这样。

    该算法的好处是它可以“早期”检测连续元素的子范围,而无需查看这些子范围。显然,它的最坏情况表现(如果没有连续的子范围)仍然是O(n)。在最好的情况下,当整个输入数组是连续的时,该算法将立即检测到它。 (我还在为这个算法做一个有意义的估算。)

    此算法的可用性再次受到唯一性要求的破坏。我不知道在你的案子里是否有“给定”的东西。

    无论如何,这是一个可能的C ++实现

    typedef std::vector<int> vint;
    typedef std::pair<vint::size_type, vint::size_type> range;
    
    class longest_sequence
    {
    public:
      const range& operator ()(const vint &v)
      { 
        current = max = range(0, 0);
    
        process_subrange(v, 0, v.size() - 1);
        check_record();
    
        return max;
      }
    
    private:
      range current, max;
    
      void process_subrange(const vint &v, vint::size_type i, vint::size_type j);
      void check_record();
    };
    
    void longest_sequence::process_subrange(const vint &v, 
                                            vint::size_type i, vint::size_type j)
    {
      assert(i <= j && v[i] <= v[j]);
      assert(i == 0 || i == current.second + 1);
    
      if (v[j] - v[i] == j - i)
      { // Consecutive subrange found
        assert(v[current.second] <= v[i]);
        if (i == 0 || v[i] == v[current.second] + 1)
          // Append to the current range
          current.second = j;
        else
        { // Range finished
          // Check against the record 
          check_record();
          // Start a new range
          current = range(i, j);
        }
      }
      else
      { // Subdivision and recursive calls
        assert(i < j);
        vint::size_type m = (i + j) / 2;
        process_subrange(v, i, m);
        process_subrange(v, m + 1, j);
      }
    }
    
    void longest_sequence::check_record()
    {
      assert(current.second >= current.first);
      if (current.second - current.first > max.second - max.first)
        // We have a new record
        max = current;
    }
    
    int main()
    {
      int a[] = { 1, 3, 4, 5, 6, 8, 9 };
      std::vector<int> v(a, a + sizeof a / sizeof *a);
      range r = longest_sequence()(v);
      return 0;
    }
    

答案 2 :(得分:1)

我相信这应该做到吗?

size_t beginStreak = 0;
size_t streakLen = 1;
size_t longest = 0;
size_t longestStart = 0;
for (size_t i=1; i < len.size(); i++) {
    if (vec[i] == vec[i-1] + 1) {
        streakLen++;
    }
    else {
        if (streakLen > longest) {
            longest = streakLen;
            longestStart = beginStreak;
        }
        beginStreak = i;
        streakLen = 1;
    }
}
if (streakLen > longest) {
    longest = streakLen;
    longestStart = beginStreak;
}

答案 3 :(得分:1)

您无法在O(N)时间内解决此问题。想象一下,您的列表是第一个N-1偶数,加上一个奇数(从第一个N-1奇数中选出)。然后在列表中的某处有一条长度为3的条纹,但最坏的情况是您需要扫描整个列表才能找到它。即使平均而言,您仍需要检查至少一半的清单才能找到它。

答案 4 :(得分:0)

与罗德里戈的解决方案类似,但也解决了你的例子:

#include <vector>
#include <cstdio>

#define len(x) sizeof(x) / sizeof(x[0])

using namespace std;

int nums[] = {1,3,4,5,6,8,9};
int streakBase = nums[0];
int maxStreakLength = 1;

void updateStreak(int currentStreakLength, int currentStreakBase) {
  if (currentStreakLength > maxStreakLength) {
    maxStreakLength = currentStreakLength;
    streakBase = currentStreakBase;
  }
}

int main(void) {
  vector<int> v;
  for(size_t i=0; i < len(nums); ++i)
    v.push_back(nums[i]);

  int lastBase = v[0], currentStreakBase = v[0], currentStreakLength = 1;

  for(size_t i=1; i < v.size(); ++i) {
    if (v[i] == lastBase + 1) {
      currentStreakLength++;
      lastBase = v[i];
    } else {
      updateStreak(currentStreakLength, currentStreakBase);
      currentStreakBase = v[i];
      lastBase = v[i];
      currentStreakLength = 1;
    }
  }
  updateStreak(currentStreakLength, currentStreakBase);
  printf("maxStreakLength = %d and streakBase = %d\n", maxStreakLength, streakBase);

  return 0;
}