每次更新时显示地图按值排序
Posted
技术标签:
【中文标题】每次更新时显示地图按值排序【英文标题】:display map every time it is updated sorted by value 【发布时间】:2009-09-29 16:34:12 【问题描述】:基本上,我有
map<std::string, int>
所以如果我有
富 5 小节 10 插孔 3在地图中,我要显示它(注意倒序)
小节 10 富 5 插孔 3每次更新时,我都想遍历所有元素,找出它们,按值排序。实现它的好方法是什么?我应该为构造函数提供一个比较器吗?
我要注意的是,地图中的值将至少更新 1 亿次,因此效率至关重要,而多余的空间也没有问题
请不要使用 Boost 解决方案...谢谢
【问题讨论】:
你说要显示按value排序的map,但是你给出的例子是key排序的。你能澄清一下吗? 你想在每次插入地图时显示内容吗? 是的,每次。好吧,我想按值排序..它也意外地按键排序。 你需要一个更好的例子。我们是否按名称排序,升序;或数字,降序?因为要执行后者,您需要map<int, string>
查看我的回答,为了保持名称的唯一性和按 int 排序,我建议使用 bimap(对于 2-indexes 情况的 Boost MultiIndex 扩展)。
【参考方案1】:
struct keyval_t std::string key; int val; ;
int operator<(const keyval_t &a, const ketval_t &b)
return a.val<b.val || (a.val==b.val && a.key<b.key);
那么你需要一张地图和一套:
map<std::string, int>; set<keyval_t>;
更新时,你需要先查找 map 以确定键值对,然后更新 map 和 set。在打印时,您只需遍历集合。就理论时间复杂度而言,这是最优的。但是,它使内存加倍。这符合您的目标吗?
为了减少内存,您可以考虑以下几点:
map<std::string,uint64_t>; set<uint64_t>;
映射的值(也是集合的键)是:(uint64_t)val。此解决方案也更快,因为它避免了字符串之间的比较。
【讨论】:
【参考方案2】:如果您想要一个按键和值排序的高性能映射,您想要Boost MultiIndex,它会在每次更新时更新(重新排序)(您必须手动执行)并且有一个很好的文档。
【讨论】:
实际上,对于地图,您需要 Boost Bimap,它(恕我直言)更易于使用。但这并没有什么神奇之处,它在幕后使用了 Boost MultiIndex。请参阅下面的答案以获取一些代码。【参考方案3】:前面的回复有不便没有考虑到初始需求(key是std::string,value是int)。
已编辑:在 cmets 之后,我想直接用 Bimap 呈现它会更好:)
所以我们开始吧!
#include <boost/bimap.hpp>
class MyMap
struct name ;
struct value ;
typedef boost::tagged<name, std::string> tagged_name;
typedef boost::tagged<value, int> tagged_value;
// unordered_set_of: guarantees only unicity (not order)
// multi_set_of: guarantees only order (not unicity)
typedef boost::bimap< boost::unordered_set_of< tagged_name >,
boost::multi_set_of< tagged_value,
std::greater< tagged_value >
>
> impl_type;
public:
// Redefine all usual types here
typedef typename impl_type::map_by<name>::const_iterator const_iterator;
typedef typename impl_type::value_type value_type;
// Define the functions you want
// the bimap will not allow mutators because the elements are used as keys
// so you may want to add wrappers
std::pair< iterator, bool > insert(const value_type & x)
std::pair< iterator, bool > result = m_impl.insert(x);
if (result.second) this->display();
return result;
// insert
iterator insert(iterator position, const value_type & x)
iterator result = m_impl.insert(x);
this->display();
return result;
// insert
template< class InputIterator >
void insert(InputIterator first, InputIterator last)
m_impl.insert(first, last);
this->display();
// insert
private:
void display() const
// Yeah I know about std::for_each...
typedef typename impl_type::map_by<value>::const_iterator const_it;
for (const_it it = m_impl.begin(), end = m_impl.end(); it != end; ++it)
// Note the inversion of the 'second' and 'first',
// we are looking at it from the right
std::cout << it->second << " " << it->first << std::endl;
impl_type m_impl;
; // class MyMap
给你。
我强烈建议您查阅 bimap 文档。存储的可能性有很多(set_of、unordered_set_of、unconstrained_set_of、muli 变体、list_of 变体……),所以可能有一种可以满足您的需求。
那么您也可以在每次显示时进行排序:
#include <set>
#include <map>
// Just use a simple std::map<std::string,int> for your impl_type
// Mutators are allowed since the elements are sorted each time you display
struct Comparator
bool operator(const value_type& lhs, const value_type& rhs) const
return lhs.second < rhs.value;
;
void display() const
typedef std::multi_set<value_type, Comparator> sort_type;
sort_type mySet;
std::copy(m_impl.begin(), m_impl.end(), std::inserter(mySet, mySet.end()));
for (sort_type it = mySet.begin(), end = mySet.end(); it != end; ++it)
std::cout << it->first<< " " << it->second << std::endl;
现在应该更容易理解我的意思了:)
【讨论】:
我认为这个问题准确地处理了这样一个事实,即虽然按键排序,但他希望它们以递减的顺序打印。除了 boost::bimap 的推荐之外,大多数或您的回答在这方面都具有误导性。【参考方案4】:嗯,你必须按键排序。 “按值排序”的最简单方法是使用切换键和值的多图。所以这是一种方法(注意——我现在无法访问编译器,所以如果它不能编译,我很抱歉):
#include <algorithm>
#include <functional>
#include <map>
#include <string>
#include <utility>
typedef std::multimap<int, std::string, std::greater<int> > MapType;
struct MapKeyOutput
void operator()(const MapType::value_type& val)
std::cout << val.second << " " << val.first << "\n";
void display_insert(MapType& m, const std::string& str, int val)
m.insert(std::make_pair(val, str));
std::for_each(m.begin(), m.end(), MapKeyOutput());
int main()
MapType m;
display_insert(m, "Billy", 5);
display_insert(m, "Johnny", 10);
您也可以创建一个在内部使用多图的地图类,我只是不想输入它。然后 display_insert 将是一些成员函数。不过,这应该证明了这一点。兴趣点:
typedef std::multimap<int, std::string, std::greater<int> > MapType;
注意比较器是greater<int>
以降序排序。我们使用的是多图,因此多个名称可以有相同的数字。
struct MapKeyOutput
void operator()(const MapType::value_type& val)
std::cout << val.second << " " << val.first << "\n";
这是一个函数对象,用于输出地图中的一个元素。 second 是在 first 之前输出,所以顺序就是你想要的。
std::for_each(m.begin(), m.end(), MapKeyOutput());
这会将我们的函数对象应用于 m 中的每个元素。
【讨论】:
旁注:这有点复杂,因为相同的名称可以添加两次。如果这是一个严重的问题,我会支持 Matthiew M 的Boost.Bimap
推荐。
好主意,但是,我将如何将比利增加 1?我不断插入键,将值递增 1。
@Andrei Chikatilo:然后你必须删除元素并重新插入它。 Boost.Bimap IIRC 也解决了这个问题。
不能使用 bimap - 它必须是可逆的 m[S]=1; m[R]=1;哎呀
@pgast- 你错了。你想要bimap< set_of<string>, multiset_of<int> >
,它的左视图为map<string, int>
,右视图为multimap<int, string>
。以上是关于每次更新时显示地图按值排序的主要内容,如果未能解决你的问题,请参考以下文章