【问题标题】:How to find in c++ STL in a specific manner?如何以特定方式在 c++ STL 中查找?
【发布时间】:2015-01-08 04:49:24
【问题描述】:
map< pair<int,int> , int > m ;

这里pair.firstpair.second 是正数,pair.second &gt;= pair.first

我想在映射m 中找到所有迭代器,这样对于给定的键。键是一个整数,位于对之间,例如键是 2,对是 [2,5][1,2] 等。
例如m[1,3] = 10 , m[3,5] = 6 , m[1,8] = 9 , m[7,8] = 15 然后当我搜索 m.find(3) 时,它会返回 m[1,3]m[1,8]m[3,5] 的迭代器。如果没有密钥,它将返回 m.end()

【问题讨论】:

  • 您要查找搜索值i 位于两个对值之间的键吗?
  • 好的。从您的问题中很难看出这一点。
  • 您的示例似乎错误。你的意思是你迭代到m[1,3]m[3,5] 而不是m[2,3]
  • “+ve”是什么意思?
  • 如果键是[1,3]、[2,4]、[3,5],你搜索3,你会找到什么?

标签: c++ algorithm search map stl


【解决方案1】:

我不确定为什么你想这样做,但是现在 Boost Interval Container 库非常有能力。

假设您可能想要跟踪特定点的映射值的 total(总和),您可以简单地将拆分 Combining Style 应用于您的输入数据和利润:

Live On Coliru

#include <boost/icl/split_interval_map.hpp>
#include <boost/range/iterator_range.hpp>
#include <iostream>

namespace icl = boost::icl;

int main() {
    using Map  = icl::split_interval_map<int, int>;
    using Ival = Map::interval_type;
    Map m;
    m.add({Ival::closed(1,3), 10});
    m.add({Ival::closed(3,5), 6});
    m.add({Ival::closed(1,8), 9});
    m.add({Ival::closed(7,8), 15});

    for (auto e : m) std::cout << e.first << " -> " << e.second << "\n";

    std::cout << "------------------------------\n";

    for (auto e : boost::make_iterator_range(m.equal_range(Ival::closed(3,3))))
        std::cout << e.first << " -> " << e.second << "\n";
}

这会告诉我们:

[1,3) -> 19
[3,3] -> 25
(3,5] -> 15
(5,7) -> 9
[7,8] -> 24
------------------------------
[3,3] -> 25

注意如何

  • 合并非常准确地反映了 [3,3] 是唯一同时与输入数据中的[1,3][3,5] 重合的点,因此,我们得到组合集中的半开区间([1,3)[3,3](3,5])。
  • 还要注意对这一点的查询如何正确返回您感兴趣的所有三个区间的10+6+9 的总和。

这有什么用?

所以,你看我把问题的焦点从“如何?”转移了。到“什么?”。它通常有助于说明代码的目标而不是特定的机制。

当然,如果您对平均值、最小值或最大值感兴趣,而不是总和,您可能会发现自己正在编写一些自定义组合策略。

奖励如果您愿意,您至少可以使用 Boost Icl 编写 OP 中提出的问题的解决方案:Live On Coliru。虽然它不是特别有效,但它直截了当且健壮。

【讨论】:

  • Coliru 示例仍然是O(N),而不是O(log N),还是我遗漏了什么? (我得到了您展示的类似解决方案,但遗憾的是 Boost.ICL 不支持对数解决方案)。
  • @TemplateRex 它似乎是 O(1)。无论我将什么标准输入/命令行参数/环境传递给程序,每个输入都需要相同的时间。我做错了吗?
【解决方案2】:

如果您只需要在映射中找到的下一个值的迭代器,您可以使用 std::find_if 算法,如下所示:

int key=2;
map<pair<int,int>, int>::iterator it =std::find_if(m.begin(),m.end(),
                         [key](pair<pair<int,int>,int> entry)
                         {
                           return (entry.first.first <= key) 
                                  && (entry.first.second >= key);
                         }
                         );

cout << "the next value is: [" << it->first.first << "/";
cout << it->first.second << "] " << it->second << endl;

【讨论】:

    【解决方案3】:

    没有办法避免从地图开始进行线性搜索,因为如果第一个元素是 {0,INT_MAX} 则它匹配并且您想要的元素不一定在连续范围内,例如如果您有 {1,3},{2,2}{3,5} 当键为 3 时,您只需要第一个和最后一个元素。

    当您到达first 大于键的元素时,您可以停止搜索。

    类似这样的东西(未经测试):

    typedef map< pair<int,int> , int > Map;
    
    std::vector<Map::iterator>
    find(int key, const Map& m)
    {
      std::vector<Map::iterator> matches;
      for (Map::iterator it = m.begin(), end = m.end(); it != end && key <= it->first; ++it)
      {
        if (it.first >= key && key <= it.second)
          matches.push_back(it);
      }
      return matches;
    }
    

    你可以把它变成一个仿函数并使用find_if,但我不确定它是否值得。

    如果您只想每次调用返回一个迭代器:

    typedef map< pair<int,int> , int > Map;
    
    Map::iterator
    find(int key, const Map& m, Map::iterator it)
    {
      for (Map::iterator end = m.end(); it != end && key <= it->first; ++it)
      {
        if (it.first >= key && key <= it.second)
          return it;
      }
      return m.end();
    }
    
    Map::iterator
    find(int key, const Map& m)
    { return find(key, m, m.begin()); }
    

    【讨论】:

    • 我需要 log(N) 时间复杂度类似于 Print BST keys in the given range 。
    • 您无法获取日志(N)。您必须从第一个元素开始搜索,并且必须检查每个元素直到m.lower_bound({key+1,0})
    【解决方案4】:

    我认为您可以存储(和使用)地图的相应键,而不是迭代器。如果是这样,那么代码可能看起来像

    #include <iostream>
    #include <map>
    #include <algorithm>
    #include <vector>
    #include <utility>
    
    int main() 
    {
        std::map<std::pair<int, int>, int> m;
        m[{ 1, 3}] = 10; 
        m[{ 3, 5}] = 6;
        m[{ 7, 8}] = 15;
    
        typedef std::map<std::pair<int, int>, int>::value_type value_type;
        typedef std::map<std::pair<int, int>, int>::key_type key_type;
    
        int search;
    
        auto in_range = [&search]( const value_type &value )
        {
            return value.first.first <= search && search <= value.first.second; 
        };
    
        search = 3;
    
        std::vector<key_type> v;
        v.reserve( std::count_if( m.begin(), m.end(), in_range ) );
    
        for ( const auto &p : m )
        {
            if ( in_range( p ) ) v.push_back( p.first );
        }
    
        for ( const auto &p : v ) 
        {
            std::cout << "[ " << p.first << ", " << p.second << " ]" << std::endl;
        }
    
        return 0;
    }
    

    输出是

    [ 1, 3 ]
    [ 3, 5 ]
    

    考虑到假设 key.first 小于或等于 key.second ,其中 key 是映射的键。

    【讨论】:

    • 这行得通,但即使每个元素都有p.first &gt; key,也会在地图上进行两次完整的传递
    • @Jonathan Wakely 在这种情况下,首先您可以使用 upper_bound 找到第一个合适的迭代器。
    • 使用upper_bound 没有任何好处,因为无论如何您都必须从头开始,您不妨从第一个元素进行线性搜索,并在到达upper_bound 的元素时停止会返回......提前找到那个元素并没有多大帮助,它会将 O(N) 变成 O(n+log(N)) (其中 N = m.size() 和 n = distance(m.begin (), m.upper_bound(...))),但你可以在 O(n) 中完成
    • 为什么不使用 copy_if 而不是循环?您使用 count_if 进行计数,使用 copy_if 进行副本似乎是合理的。比如:copy_if(begin(m), end(b), back_inserter(v), in_range)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-09-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-26
    • 2016-07-03
    • 1970-01-01
    相关资源
    最近更新 更多