我需要按值排序std::map
,然后按键排序。地图包含以下数据:
1 realistically
8 really
4 reason
3 reasonable
1 reasonably
1 reassemble
1 reassembled
2 recognize
92 record
48 records
7 recs
我需要按顺序获取值,但是关键是在值按顺序排列后,键需要按字母顺序排列。我怎么能这样做?
答案 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::pair
(first
),然后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
注意:自C++17起,您可以将range-based for loops与structured bindings一起用于遍历地图。 结果,用于复制地图的代码变得更短,更易读:
for (auto const &[k, v] : m)
s.emplace(v, k); // Flip the pairs.