每次按值排序更新时显示地图

时间:2009-09-29 16:34:12

标签: c++ algorithm stl

基本上,我有

map<std::string, int>

所以,如果我有

  • foo 5
  • bar 10
  • jack 3

在地图中,我想显示它(注意相反的顺序)

  • bar 10
  • foo 5
  • jack 3

每次更新时,我都希望迭代所有元素,然后按值排序。实现这个的好方法是什么?我应该为构造函数提供比较器吗?

我想注意地图中的值将至少更新1亿次,因此效率至关重要,因为额外空间没有问题

请不要提升解决方案... thx

4 个答案:

答案 0 :(得分:2)

如果你想要一个按键和值排序的高性能地图,你需要Boost MultiIndex,它会在每次更新时(你必须手动完成)更新(使用)并且有一个很好的文档。

答案 1 :(得分:2)

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<std::string,uint64_t>; set<uint64_t>;

映射的值(也是集合的键)是:(uint64_t)val&lt;&lt; 32 | counter,其中counter是区分相同值的东西。例如,无论何时插入密钥,都将计数器增加1.更新值时无需更新计数器。如果您不喜欢uint64_t,请使用pair&lt; int,int&gt;代替。此解决方案也更快,因为它避免了字符串之间的比较。

答案 2 :(得分:1)

以前的回复不便不考虑初始要求(键是std :: string,值是int)。

编辑:在评论之后,我想直接用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;
  }
}

现在应该更容易理解我的观点:)

答案 3 :(得分:0)

嗯,你必须按键排序。 “按值排序”的最简单方法是使用带有键和值切换的多图。所以这是一种方法(注意 - 我现在无法访问编译器,所以如果它不编译,我很抱歉):

#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";
    }
}

这是一个在地图中输出一个元素的函数对象。第二个在第一个之前输出,所以订单就是你想要的。

std::for_each(m.begin(), m.end(), MapKeyOutput());

这将我们的函数对象应用于m。

中的每个元素