【问题标题】:C++ adjacent valuesC++ 相邻值
【发布时间】:2021-05-27 06:01:22
【问题描述】:

我看到了以下问题,虽然设法在 O(n^2) 内解决了它,但我认为我们可以做得更好。

给定一个整数向量,返回不相邻的最大个数 类似号码的副本。

例如,给定:[9,2,3,4,0,4,5,6,0,8]

我们可以看到我们有2个不相邻的0和2个不相邻的4(其余都是1)所以答案是max(2,2)=2;

给定:[4,4,4,4,4]

我们有 3 个不相邻的 4,所以答案是 3。 我们先取 4 个,然后我们不能取它旁边的一个(它们是相邻的),所以我们取后面的一个,依此类推。

我的解决方案: 遍历向量并检查我们从当前数字中看到了多少非相邻副本,如果它大于当前最大值,我们相应地更新最大值。

问:我们怎样才能更有效地解决这个问题?

【问题讨论】:

  • 问题是什么?
  • 抱歉,如果我对“我认为我们可以做得更好”不清楚。可以更有效地解决这个问题吗?
  • map<int,int> counts; for i in 0 to size: if v[i] is different than v[i - 1] then counts[v[i]]++; return max entry in counts
  • @Jeffrey 可能我误读了,但这不会导致[4,4,4,4,4] 的结果为 1 而不是 3?
  • 是的,我的意思是,对于所有值,如果它与以前的不同或者如果您跳过以前因为它没有不同增量计数器

标签: c++ algorithm vector


【解决方案1】:

如果您对使用 O(n) 空间感到满意,您可以使用存储桶来存储您看到的值以实现 O(n) 运行时。

最简单的实现方法是使用地图。即:

std::pair<int, int> max_adj(std::vector<int> arr) {
    std::map<int, int> buckets;
    
    for(int x = 0; x < arr.size(); x++) {
        // Loop to eat up adjacent values
        int in_a_row = 1;
        for(int curr = x++;
            x < arr.size() && arr[curr] == arr[x];
            x++, in_a_row++) { }
        x--; // The loop will increment x one past last dupe, so go back
        
        // Only count every other adjacent value seen
        buckets[arr[x]] += (in_a_row + 1) / 2;
    }
    
    // Find the most seen
    std::map<int, int>::iterator mx = std::max_element
    (
        buckets.begin(), buckets.end(),
        [] (const std::pair<int, int> &p1, const std::pair<int, int> &p2) {
            return p1.second < p2.second;
        }
    );
    
    if(mx != buckets.end()) {
        return *mx;
    }
    return std::pair<int, int>(-1, -1);
}

现场示例:https://ideone.com/v9OOIA

【讨论】:

  • std::max_element 可能会返回您无法取消引用的内容,如果 arr.size() is 0
  • 使用[1 2 1 1],如果我能很好地理解您的算法,您将得到 1 的答案。我期待的答案是 2,但我不确定是否理解这个问题!
  • @Jeff 好电话。我进行了编辑以涵盖这一点。谢谢!
  • @Damien 根据我对问题的理解,这是正确的。 1 应该计算两次:一次用于索引 0,一次用于索引 2(但不用于索引 3!)
【解决方案2】:

这个问题可以通过创建一个包含数字及其索引的数组来解决,如下所示,

原始数组 = [9, 2, 3, 4, 0, 4, 5, 6, 0, 8],
数字索引数组 = [9, 0], [2, 1], [3, 2], [4, 3], [0, 4], [4, 5 ], [5, 6], [6, 7], [0, 8], [8, 9]

这里std::pair 的第一个元素是数字本身,第二个元素是数字的索引。
之后,将此数组按数字排序,结果如下,
[0, 8], [0, 4], [2, 1], [3, 2], [4, 5], [4, 3], [@ 987654339@, 6], [6, 7], [8, 9], [9, 0]

排序的目的是使相同的数字彼此相邻。一旦相同的数字相邻,就需要简单的迭代来找到答案。让我们看看它是如何工作的,

排序数组中的第一个数字是0,通过找到0 的upper bound 将给出所有相邻的0,这意味着以下将可用,
[0, 8], [0, 4]
但是正如您在上面看到的,这些条目不是按照索引4 的索引条目的顺序排列在索引8 的条目之后,这是因为std::sort 不是一个稳定的排序,这意味着元素的相对顺序不会被保留.所以在继续之前,这些元素必须再次按索引排序,结果将是

[0, 4], [0, 8]
如上所示索引4和8不相邻4 + 1 != 8,非相邻数字的计数为two。

检查0 的所有条目,算法将考虑下一个数字2,但正如您在上面看到的数组中只有一个数字2 的条目[2, 1] 并且由于这个下一个数字将考虑一下,所有号码都会发生同样的情况,所以让我们直接查看号码4,它有多个条目,如下所示,
[4, 5], [4, 3]
然后按索引对这些条目进行排序,

[4, 3], [4, 5]
这里再次索引不是相邻索引3 + 1 != 5,非相邻数的计数是two。相同的过程将应用于所有数字,最终答案将是最大非相邻数字count = 2

对于数组[4, 4, 4, 4, 4]的其他示例,它的最终编号和索引数组将是,

[4, 0], [4, 1], [4, 2], [4, 3], [4, 4]
如您所见,索引0、2、4 不是相邻索引和最大非相邻数count = 3

#include <iostream>

#include <vector>
#include <algorithm>

using std::cout;

std::size_t countNonAdjacentEntries(std::vector<std::pair<int, std::size_t>>::const_iterator firstIt,
                                    std::vector<std::pair<int, std::size_t>>::const_iterator lastIt){


    std::size_t count = 0;

    for(std::vector<std::pair<int, std::size_t>>::const_iterator preIt = firstIt, it = preIt + 1; lastIt != it;){

        if(preIt->second + 1 != it->second){

            ++count;

            preIt = it;
            ++it;
        }
        else{
            ++it;
        }
    }

    ++count;
    return count;
}

std::size_t maxNonAdjacentNumber(const std::vector<int>& numbers){

    std::vector<std::pair<int, std::size_t>> numAndIndex;
    numAndIndex.reserve(numbers.size());

    for(std::vector<int>::size_type i = 0, numbersCount = numbers.size(); i < numbersCount; ++i){

        numAndIndex.emplace_back(numbers[i], i);
    }

    std::sort(numAndIndex.begin(), numAndIndex.end(),
              [](const std::pair<int, std::size_t>& a, const std::pair<int, std::size_t>& b){
        return a.first < b.first;});

    std::size_t count = 0;

    for(std::vector<std::pair<int, std::size_t>>::const_iterator it = numAndIndex.cbegin(), endIt = numAndIndex.cend();
        endIt != it; ){

        std::vector<std::pair<int, std::size_t>>::const_iterator upBoundIt = std::upper_bound( it + 1, endIt, it->first,
                    [](int val, const std::pair<int, std::size_t>& ele){return val < ele.first;});

        if(upBoundIt - it > 1){

            std::sort(numAndIndex.begin(), numAndIndex.end(),
                      [](const std::pair<int, std::size_t>& a, const std::pair<int, std::size_t>& b){
                return a.second < b.second;});

            count = std::max(count, countNonAdjacentEntries(it, upBoundIt));
            it = upBoundIt;
        }
        else{
            ++it;
        }
    }

    return count;
}

int main(){

    cout<< "[9, 2, 3, 4, 0, 4, 5, 6, 0, 8] => "<< maxNonAdjacentNumber({9, 2, 3, 4, 0, 4, 5, 6, 0, 8})<< '\n';
    cout<< "[4, 4, 4, 4, 4] => "<< maxNonAdjacentNumber({4, 4, 4, 4, 4})<< '\n';
}

输出

[9, 2, 3, 4, 0, 4, 5, 6, 0, 8] => 2
[4, 4, 4, 4, 4] => 3

【讨论】:

    【解决方案3】:

    独特的删除重复,排序剩余的丛,然后找到最长的运行。

    template<class T, class A>
    std::optional<T> most_runs_of( std::vector<T, A> r ) {
      if (r.empty()) return std::nullopt;
      r.erase( std::unique( begin(r), end(r) ), end(r) );
      std::sort( begin(r), end(r) );
      T winner = r.front();
      std::size_t winner_count = 0;
      T const* cur = nullptr;
      std::size_t cur_count = 0;
      for (T const& e:r) {
        if (e == winner)
        {
          ++winner_count;
          continue;
        }
        if (cur && *cur==e) {
          ++cur_count;
          if (cur_count > winner_count) {
            winner = e;
            winner_count = cur_count;
            cur = nullptr;
            continue;
          }
        }
        cur = std::addressof(e);
        cur_count = 1;
      }
      return winner;
    }
    

    这会复制 std::vector 并在 O(nlgn) 和 O(n) 空间的副本中就地执行它。

    如果你调用上述方法后不需要std::vector,则将std::move 加入算法中。

    【讨论】:

    • 相邻的部分呢?
    • Eliminates all except the first element from every consecutive group 所以 OP 的第二个示例 [4,4,4,4,4] 将减少到 [4]?
    猜你喜欢
    • 2014-07-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-15
    • 2019-05-16
    • 2023-03-04
    • 2021-09-22
    相关资源
    最近更新 更多