我如何先按值排序std :: map,然后按键排序?

时间:2013-11-07 17:01:03

标签: c++ algorithm sorting dictionary key

我需要按值排序std::map,然后按键排序。地图包含以下数据:

  1  realistically
  8         really
  4         reason
  3     reasonable
  1     reasonably
  1     reassemble
  1    reassembled
  2      recognize
 92         record
 48        records
  7           recs

我需要按顺序获取值,但是关键是在值按顺序排列后,键需要按字母顺序排列。我怎么能这样做?

5 个答案:

答案 0 :(得分:61)

std::map将按keys对其元素进行排序。排序时它不关心values

您可以使用std::vector<std::pair<K,V>>然后使用std::sort后跟std::stable_sort对其进行排序:

std::vector<std::pair<K,V>> items;

//fill items

//sort by value using std::sort
std::sort(items.begin(), items.end(), value_comparer);

//sort by key using std::stable_sort
std::stable_sort(items.begin(), items.end(), key_comparer);

第一种排序应使用std::sort,因为它是nlog(n),然后在最坏的情况下使用std::stable_sort n(log(n))^2

请注意,虽然出于性能原因选择std::sort,但正确排序需要std::stable_sort,因为您希望保留按值排序。


@gsf在评论中注明,如果您选择首先比较std::sort的比较器,并且如果它们相等,则可以使用 values,排序{ {1}}。

keys

这应该是有效的。

但是等等,有一个更好的方法:存储auto cmp = [](std::pair<K,V> const & a, std::pair<K,V> const & b) { return a.second != b.second? a.second < b.second : a.first < b.first; }; std::sort(items.begin(), items.end(), cmp); 而不是std::pair<V,K>,然后根本不需要任何比较器 - std::pair<K,V>的标准比较器就足够了,首先比较std::pairfirst),然后V second

K

这应该很有效。

答案 1 :(得分:14)

您可以使用std::set代替std::map

您可以在std::pair中存储密钥和值,容器类型如下所示:

std::set< std::pair<int, std::string> > items;

std::set会根据原始密钥和std::map中存储的值对其值进行排序。

答案 2 :(得分:1)

std::map已使用您定义的谓词对值进行排序,或者std::less如果您未提供谓词。 std::set还将按照定义比较器的顺序存储项目。但是,set和map都不允许你有多个键。如果您想单独使用数据结构来完成此操作,我建议您定义一个std::map<int,std::set<string>。您还应该意识到字符串的std::less将按字典顺序排序,而不是按字母顺序排序。

答案 3 :(得分:0)

编辑:其他两个答案很重要。我假设您想要将它们订购到其他结构中,或者为了将它们打印出来。

“最佳”可能意味着许多不同的事情。你的意思是“最简单”,“最快”,“最有效”,“最少代码”,“最具可读性?”

最明显的方法是循环两次。在第一遍中,订购值:

if(current_value > examined_value)
{
    current_value = examined_value
    (and then swap them, however you like)
}

然后在第二遍中,按字母顺序排列单词,但前提是它们的值匹配。

if(current_value == examined_value)
{
    (alphabetize the two)
}

严格地说,这是一种“冒泡排序”,因为每次进行交换时都需要重新开始。当你通过整个列表而没有进行任何交换时,一个“通行证”就完成了。

还有其他排序算法,但原理是相同的:按值排序,然后按字母顺序排列。

答案 4 :(得分:0)

Nawaz的 answer中所述,您无法根据需要对地图进行单独排序,因为std::map仅根据键对元素进行排序。因此,您需要一个不同的容器,但是如果您必须坚持自己的地图,那么仍然可以(临时)将其内容复制到另一个数据结构中。

我认为,最好的解决方案是使用std::set存储翻转的键值对,如 ks1322的 answer所示。 std::set是默认排序的,order of the pairs正是您所需要的:

  

3)如果lhs.first<rhs.first,则返回true。否则,如果rhs.first<lhs.first,则返回false。否则,如果lhs.second<rhs.second,则返回true。否则,返回false

这样,您不需要额外的排序步骤,结果代码也很短:

std::map<std::string, int> m;  // Your original map.
m["realistically"] = 1;
m["really"]        = 8;
m["reason"]        = 4;
m["reasonable"]    = 3;
m["reasonably"]    = 1;
m["reassemble"]    = 1;
m["reassembled"]   = 1;
m["recognize"]     = 2;
m["record"]        = 92;
m["records"]       = 48;
m["recs"]          = 7;

std::set<std::pair<int, std::string>> s;  // The new (temporary) container.

for (auto const &kv : m)
    s.emplace(kv.second, kv.first);  // Flip the pairs.

for (auto const &vk : s)
    std::cout << std::setw(3) << vk.first << std::setw(15) << vk.second << std::endl;

输出:

  1  realistically
  1     reasonably
  1     reassemble
  1    reassembled
  2      recognize
  3     reasonable
  4         reason
  7           recs
  8         really
 48        records
 92         record

Code on Ideone

注意:自C++17起,您可以将range-based for loopsstructured bindings一起用于遍历地图。 结果,用于复制地图的代码变得更短,更易读:

for (auto const &[k, v] : m)
    s.emplace(v, k);  // Flip the pairs.