std::map
を値で、次にキーでソートする必要があります。マップには次のようなデータが含まれます。
1 realistically
8 really
4 reason
3 reasonable
1 reasonably
1 reassemble
1 reassembled
2 recognize
92 record
48 records
7 recs
私は値を順番に取得する必要がありますが、キッカーは値が順番に並んだ後にキーがアルファベット順になる必要があるということです。これどうやってするの?
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);
最初のソートは、nlog(n)
であるためstd::sort
を使用し、最悪の場合はn(log(n))^2
であるstd::stable_sort
を使用する必要があります。
パフォーマンス上の理由でstd::sort
が選択されていますが、正しい順序付けにはstd::stable_sort
が必要であり、値による順序を保持する必要があることに注意してください。
コメントに記載されている@gsfでは、最初にvalues
を比較する比較器を選択し、等しい場合はkeys
をソートする場合、onlystd::sort
を使用できます。
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
):
std::vector<std::pair<V,K>> items;
//...
std::sort(items.begin(), items.end());
それはうまくいくはずです。
std::set
の代わりにstd::map
を使用できます。
キーと値の両方をstd::pair
に保存でき、コンテナのタイプは次のようになります。
std::set< std::pair<int, std::string> > items;
std::set
は、元のキーとstd::map
に格納された値の両方で値をソートします。
Nawazのanswer で説明されているように、std::map
は並べ替えるので、マップを必要に応じて並べ替えることはできませんキーのみに基づいた要素。そのため、別のコンテナが必要ですが、マップに固執する必要がある場合は、そのコンテンツを(一時的に)別のデータ構造にコピーできます。
最善の解決策は、ks1322'sanswer に示されているように、反転したキーと値のペアを格納するstd::set
を使用することだと思います=。 std::set
はデフォルトでソートされ、 ペアの順序 は必要なとおりです:
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 から 範囲ベースのforループ を 構造化バインディング とともに使用して、マップを反復処理できます。その結果、マップをコピーするためのコードはさらに短くなり、読みやすくなります。
for (auto const &[k, v] : m)
s.emplace(v, k); // Flip the pairs.
std::map
は、定義した述語を使用して値を既に並べ替えているか、指定しない場合はstd::less
を使用して値を並べ替えます。 std::set
は、定義コンパレータの順序でアイテムも保存します。ただし、セットもマップも、複数のキーを持つことはできません。データ構造のみを使用してこれを達成する場合は、std::map<int,std::set<string>
を定義することをお勧めします。また、文字列のstd::less
がアルファベット順ではなく辞書順でソートされることも理解する必要があります。
編集:他の2つの答えは良い点を示しています。私はあなたが他の構造にそれらを注文するか、それらを印刷することを望むと仮定しています。
「最高」とは、さまざまなことを意味します。 「最も簡単」、「最速」、「最も効率的」、「最も少ないコード」、「最も読みやすい」という意味ですか?
最も明白なアプローチは、2回ループすることです。最初のパスで、値を順序付けます:
if(current_value > examined_value)
{
current_value = examined_value
(and then swap them, however you like)
}
次に、2番目のパスで、値が一致する場合にのみ、単語をアルファベット順にします。
if(current_value == examined_value)
{
(alphabetize the two)
}
厳密に言えば、これは「バブルソート」であり、スワップを行うたびに最初からやり直す必要があるため低速です。スワップを行わずにリスト全体を通過すると、1つの「パス」が終了します。
他の並べ替えアルゴリズムもありますが、原則は同じです。値で並べ替えてからアルファベット順にします。