C++ Bimap Left unordered_map Right sorted mutable multimap
C++ Bimap Left unordered_map Right sorted mutable multimap
我需要为我的项目实现以下数据结构。我有
的关系
const MyClass*
到
uint64_t
我想为每个指针保存一个与之相连的计数器,它可以随着时间的推移而改变(实际上只是递增)。这没问题,我可以简单地将它存储在 std::map 中。问题是我需要快速访问具有最高值的指针。
这就是我得出使用 boost::bimap 的结论的原因。我的项目定义如下:
typedef boost::bimaps::bimap<
boost::bimaps::unordered_set_of< const MyClass* >,
boost::bimaps::multiset_of< uint64_t, std::greater<uint64_t> >
> MyBimap;
MyBimap bimap;
这可以正常工作,但我不能修改插入一次的对上的 uint64_t 是对的吗?文档说 multiset_of 是常量,因此我不能更改 bimap 中 pair 的值。
我能做什么?更改此 bimap 中一个键的值的正确方法是什么?或者是否有更简单的数据结构可以解决这个问题?
这是一个简单的手工解决方案。
它在内部保留一个映射来存储由对象指针索引的计数,以及一组进一步的多组迭代器,按指针的降序排列。
无论何时修改计数,都必须重新索引。我已经完成了这个零碎的,但你可以根据需要作为批量更新来完成。
请注意,在 c++17 中,建议对集合和映射进行 splice
操作,这将使重新索引非常快。
#include <map>
#include <set>
#include <vector>
struct MyClass { };
struct store
{
std::uint64_t add_value(MyClass* p, std::uint64_t count = 0)
{
add_index(_map.emplace(p, count).first);
return count;
}
std::uint64_t increment(MyClass* p)
{
auto it = _map.find(p);
if (it == std::end(_map)) {
// in this case, we'll create one - we could throw instead
return add_value(p, 1);
}
else {
remove_index(it);
++it->second;
add_index(it);
return it->second;
}
}
std::uint64_t query(MyClass* p) const {
auto it = _map.find(p);
if (it == std::end(_map)) {
// in this case, we'll create one - we could throw instead
return 0;
}
else {
return it->second;
}
}
std::vector<std::pair<MyClass*, std::uint64_t>> top_n(std::size_t n)
{
std::vector<std::pair<MyClass*, std::uint64_t>> result;
result.reserve(n);
for (auto idx = _value_index.begin(), idx_end = _value_index.end() ;
n && idx != idx_end ;
++idx, --n) {
result.emplace_back((*idx)->first, (*idx)->second);
}
return result;
}
private:
using map_type = std::map<MyClass*, std::uint64_t>;
struct by_count
{
bool operator()(map_type::const_iterator l, map_type::const_iterator r) const {
// note: greater than orders by descending count
return l->second > r->second;
}
};
using value_index_type = std::multiset<map_type::iterator, by_count>;
void add_index(map_type::iterator iter)
{
_value_index.emplace(iter->second, iter);
}
void remove_index(map_type::iterator iter)
{
for(auto range = _value_index.equal_range(iter);
range.first != range.second;
++range.first)
{
if (*range.first == iter) {
_value_index.erase(range.first);
return;
}
}
}
map_type _map;
value_index_type _value_index;
};
我需要为我的项目实现以下数据结构。我有
的关系const MyClass*
到
uint64_t
我想为每个指针保存一个与之相连的计数器,它可以随着时间的推移而改变(实际上只是递增)。这没问题,我可以简单地将它存储在 std::map 中。问题是我需要快速访问具有最高值的指针。
这就是我得出使用 boost::bimap 的结论的原因。我的项目定义如下:
typedef boost::bimaps::bimap<
boost::bimaps::unordered_set_of< const MyClass* >,
boost::bimaps::multiset_of< uint64_t, std::greater<uint64_t> >
> MyBimap;
MyBimap bimap;
这可以正常工作,但我不能修改插入一次的对上的 uint64_t 是对的吗?文档说 multiset_of 是常量,因此我不能更改 bimap 中 pair 的值。
我能做什么?更改此 bimap 中一个键的值的正确方法是什么?或者是否有更简单的数据结构可以解决这个问题?
这是一个简单的手工解决方案。
它在内部保留一个映射来存储由对象指针索引的计数,以及一组进一步的多组迭代器,按指针的降序排列。
无论何时修改计数,都必须重新索引。我已经完成了这个零碎的,但你可以根据需要作为批量更新来完成。
请注意,在 c++17 中,建议对集合和映射进行 splice
操作,这将使重新索引非常快。
#include <map>
#include <set>
#include <vector>
struct MyClass { };
struct store
{
std::uint64_t add_value(MyClass* p, std::uint64_t count = 0)
{
add_index(_map.emplace(p, count).first);
return count;
}
std::uint64_t increment(MyClass* p)
{
auto it = _map.find(p);
if (it == std::end(_map)) {
// in this case, we'll create one - we could throw instead
return add_value(p, 1);
}
else {
remove_index(it);
++it->second;
add_index(it);
return it->second;
}
}
std::uint64_t query(MyClass* p) const {
auto it = _map.find(p);
if (it == std::end(_map)) {
// in this case, we'll create one - we could throw instead
return 0;
}
else {
return it->second;
}
}
std::vector<std::pair<MyClass*, std::uint64_t>> top_n(std::size_t n)
{
std::vector<std::pair<MyClass*, std::uint64_t>> result;
result.reserve(n);
for (auto idx = _value_index.begin(), idx_end = _value_index.end() ;
n && idx != idx_end ;
++idx, --n) {
result.emplace_back((*idx)->first, (*idx)->second);
}
return result;
}
private:
using map_type = std::map<MyClass*, std::uint64_t>;
struct by_count
{
bool operator()(map_type::const_iterator l, map_type::const_iterator r) const {
// note: greater than orders by descending count
return l->second > r->second;
}
};
using value_index_type = std::multiset<map_type::iterator, by_count>;
void add_index(map_type::iterator iter)
{
_value_index.emplace(iter->second, iter);
}
void remove_index(map_type::iterator iter)
{
for(auto range = _value_index.equal_range(iter);
range.first != range.second;
++range.first)
{
if (*range.first == iter) {
_value_index.erase(range.first);
return;
}
}
}
map_type _map;
value_index_type _value_index;
};