【问题标题】:Find the lowest key pair for the first element in the pair using STL Map使用 STL Map 查找对中第一个元素的最低密钥对
【发布时间】:2013-09-27 00:17:58
【问题描述】:

我有一个映射,其键是一对 std::map<std::pair<int, int>, struct A> myMap。如何查找和访问该对中每个唯一第一个元素的最低对?例如,

struct A a;
myMap.insert(std::make_pair(std::pair<int, int>(1, 200), a));
myMap.insert(std::make_pair(std::pair<int, int>(1, 202), a));
myMap.insert(std::make_pair(std::pair<int, int>(2, 198), a));
myMap.insert(std::make_pair(std::pair<int, int>(2, 207), a));

我想使用的键是 和 。我不需要它们一起返回,我只需要对它们进行操作即可。

感谢您的宝贵时间!

【问题讨论】:

  • 如果您不想通过地图检测第一个元素何时发生变化,我认为您需要一张单独的地图来告诉您这些位置的位置。
  • 如果这很重要 - 为什么不在插入列表时存储最小对。当删除任何一个标记时,该值是无效的,因此进行搜索或在那里进行搜索然后

标签: c++ map iterator key std-pair


【解决方案1】:

我会选择最直接的解决方案:

auto previous_first = 0;
auto is_first_iteration = true;
for (const auto& key_value : myMap) {
  const auto& key = key_value.first;
  const auto& value = key_value.second;
  if (is_first_iteration || key.first != previous_first) {
    is_first_iteration = false;
    previous_first = key.first;
    // do something here!
  }
}

在这里,您只需简单地遍历每个元素(我们依赖于 std::map 的属性来对元素进行排序。并且对本身按第一个元素排序,然后按第二个元素排序)。在每个步骤中,我们都会记住之前的第一个元素 - 如果在这一步中相同,我们就跳过这一步。

@AndrewDurward 指出这个问题可以在对数时间内解决。这只是部分正确。首先,这个问题只有在最好的情况下才能在对数时间内解决。如果您有 N 个元素并且每个元素都有不同的 first 怎么办?您的答案中有 N 个元素,显然,您不能在对数时间内输出 N 个元素。

【讨论】:

  • 需要注意的是,这是一个线性解决方案,而问题可以在对数时间内解决。这就像调查字典中的每个单词以找到以每个字母开头的第一个条目。
  • 这只是部分正确。首先,这个问题只有在最好的情况下才能在对数时间内解决。如果您有 N 个元素并且每个元素都有不同的 first 怎么办?显然,您不能在对数时间内输出 N 个元素。第二点是您提出的解决方案不起作用,因为map::iterator 不适用于std::upper_bound。这段代码甚至无法编译——map 的迭代器不是std::upper_bound 所需的 RandomAccessIterator。是的,你仍然可以实现它,但算法会更难,而且在大多数情况下没那么有用。
  • 对,我认为替代解决方案是O(lg N),但您正确地指出它实际上是O(k lg N),其中 k 是键中唯一的第一个元素的数量。显然O(N)O(k lg N) 之间的选择将取决于kN 之间的比率。然而,关于std::upper_bound,它只需要迭代器来模拟ForwardIterator,因此保证可以与std::map::iterator一起工作——参见here
  • 是的,这是我的错误。我只是不知道upper_bound 如何在线性时间内用 ForwardIterator 实现,这就是答案 - 它不是! std::upper_bound 以线性时间作用于 std::map(或各种 ForwardIterator 容器)。基本上你的解决方案和我的一样
  • 虽然 cplusplus.com 不是最好的参考,但这里引用一句:在非随机访问迭代器上,迭代器的前进平均会产生 N 的额外线性复杂度。
【解决方案2】:

您可以使用自定义比较器

struct comp {
bool operator()(const std::pair<int, int>&x, const std::pair<int, int>& y ) const
{
    return x.second < y.second;
}
};

std::map&lt;std::pair&lt;int, int&gt;, struct A,comp &gt; myMap;

然后找到然后将find_if 与对的第一个元素一起使用。

在您的情况下,std::less&lt;T&gt; 默认按预期排序。

因此,如果没有自定义比较器,即仅使用

,以下操作将有效

std::map&lt;std::pair&lt;int, int&gt;, struct A &gt; myMap;

int search_id=1; //First Element of pair, you can use entire pair too, 
//but that will defeat the purpose of "lowest pair"
auto it=std::find_if(myMap.begin() , myMap.end() , 
                [search_id](const std::pair<std::pair<int, int>, A>& x)
                { return x.first.first == search_id; } 
                );

if(it != myMap.end())
{
std::cout<<it->first.first<<" "<<it->first.second;
}

编辑:您可以将其用作循环遍历所有元素的函数

【讨论】:

  • @sasha.sochka 我不明白你的问题,我不是来写 OP 的代码,事实上 OP 可以为每个元素使用它。在你问之前,我没有拒绝你的投票。事实上,您甚至不知道 find_if 如何用于 STL 容器
【解决方案3】:

有了一个小的重载助手,你可以使用std::lower_boundLive on Coliru

列出的第一个匹配项将是您查找的匹配项(std::pair&lt;&gt; 已按 (first,right) 排序,升序)。

在这种情况下,助手是:

struct ByKeyFirst final { int value; };
inline bool operator<(Map::value_type const& v, ByKeyFirst target) { 
    return v.first.first < target.value; 
}

如您所见,唯一“增加的复杂性”是检查是否找到了匹配项,但效率应该没问题。而且您始终可以将复杂性隐藏在(可单元测试的)帮助程序中:

Map::const_iterator byKeyFirst(Map const& map, int target)
{
    auto const e = end(map);
    auto lbound = lower_bound(begin(map), e, ByKeyFirst { target });
    if (lbound != e && lbound->first.first == target)
        return lbound;
    return e;
}

现在查找代码变成了:

int main()
{
    const Map myMap { 
        { { 1, 200 }, {} },
        { { 1, 202 }, {} },
        { { 2, 198 }, {} },
        { { 2, 207 }, {} },
    };

    auto match = byKeyFirst(myMap, 2);

    if (end(myMap) != match)
        std::cout << "Matching key: (" << match->first.first << "," << match->first.second << ")\n";
}

完整演示

#include <map>
#include <tuple>
#include <limits>

using namespace std;

struct A {};

using Pair = pair<int,int>;
using Map  = map<Pair, A>;

namespace /*anon*/
{
    struct ByKeyFirst final { int value; };
    inline bool operator<(Map::value_type const& v, ByKeyFirst target) { return v.first.first < target.value; }

    Map::const_iterator byKeyFirst(Map const& map, int target)
    {
        // you can just find the first match, Pair is already sorted by `first`, then `second`:
        auto const e = end(map);
        auto lbound = lower_bound(begin(map), e, ByKeyFirst { target });
        if (lbound != e && lbound->first.first == target)
            return lbound;
        return e;
    }
}

#include <iostream>

int main()
{
    const Map myMap { 
        { { 1, 200 }, {} },
        { { 1, 202 }, {} },
        { { 2, 198 }, {} },
        { { 2, 207 }, {} },
    };

    auto match = byKeyFirst(myMap, 2);

    if (end(myMap) != match)
        std::cout << "Matching key: (" << match->first.first << "," << match->first.second << ")\n";
}

【讨论】:

    【解决方案4】:

    由于映射键是按字典顺序排序的,因此它们也可以被视为按其第一个元素排序(尽管有一些重复)。这意味着只要我们提供适当的谓词,我们就可以利用任何在排序范围上运行的标准算法。在这种情况下,比较key的第一个元素的谓词可以写成如下:

        typedef std::map<std::pair<int, int>, struct A> myMap_t;
    
        auto compare_first = [](
            myMap_t::value_type const & p1,
            myMap_t::value_type const & p2 )
        {
            return p1.first.first < p2.first.first;
        };
    

    完成后,我们只需要选择正确的算法来迭代所需的地图元素。我们想要地图中的第一个元素,然后是与我们的谓词定义的那个不“等效”的第一个元素。这正是std::upper_bound 所做的。

    auto e1 = myMap.begin();
    auto e2 = std::upper_bound( e1, myMap.end(), *e1, compare_first );
    

    或者我们可以遍历它们:

    for( auto it = myMap.begin(); it != myMap.end(); it = std::upper_bound( it, myMap.end(), *it, compare_first ) )
            std::cout << it->first.first << " " << it->first.second << "\n";
    

    完整代码是here

    【讨论】:

    • std::upper_bound 对于 ForwardIterator 集合来说是 O(N)
    • @sasha.sochka 这并不完全准确。 ForwardIterators 的迭代器增量可能是 O(N),但标准要求它在执行的比较次数上永远不会比 O(lg N) 差(参见第 25.4.3.2 节)。
    • 是的,但是 O(N) 增量 + O(lgN) 比较 = O(N) 操作。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-07-31
    • 1970-01-01
    • 2019-11-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-11
    相关资源
    最近更新 更多