web-dev-qa-db-ja.com

インデックスによるマップ値へのアクセス

私のような構造がある場合

std::map<string, int> myMap;
myMap["banana"] = 1;
myMap["Apple"] = 1;
myMap["orange"] = 1;

MyMap [0]にアクセスするにはどうすればよいですか?

マップが内部的にソートされることはわかっていますが、これで問題ありません。マップ内の値をインデックスで取得します。 myMap [0]を試しましたが、エラーが発生します。

Error   1   error C2679: binary '[' : no operator found which takes a right-hand operand of type 'int' (or there is no acceptable conversion)   

私は私がこのようなことをすることができると気づきます:

string getKeyAtIndex (int index){
    map<string, int>::const_iterator end = myMap.end(); 

    int counter = 0;
    for (map<string, int>::const_iterator it = myMap.begin(); it != end; ++it) {
        counter++;

        if (counter == index)
            return it->first;
    }
}

しかし、確かにこれは非常に非効率的ですか?もっと良い方法はありますか?

18
Sam

あなたのmapはそのようにアクセスされることを想定しておらず、位置ではなくキーによってインデックスが付けられています。 mapイテレータはlistと同様に双方向であるため、使用している関数は、位置によってlistにアクセスするよりも非効率的ではありません。関数は、std::advance( iter, index )から始まるbegin()のヘルプを使用して記述できます。位置によるランダムアクセスが必要な場合は、vectorまたはdequeを使用します。

24
K-ballo

目標を達成するための実装固有の(移植不可能な)メソッドがあるかもしれませんが、移植性のあるメソッドはありません。

一般に、std::mapはバイナリツリーのタイプとして実装され、通常はキーでソートされます。最初の要素の定義は、順序によって異なります。また、定義では、element [0]はツリーの一番上のノードですか、それとも一番左のリーフノードですか?

多くのバイナリツリーはリンクリストとして実装されます。要素5を見つけるには、リンクをたどる必要があるため、ほとんどのリンクリストには配列のように直接アクセスできません。これは定義によるものです。

std::vectorstd::mapの両方を使用して問題を解決できます:

  1. 動的メモリからオブジェクトを割り当てます。
  2. ポインタをキーとともにstd::mapに格納します。
  3. ポインタをstd::vectorの目的の位置に格納します。

std::mapを使用すると、キーでオブジェクトに効率的にアクセスできます。
std::vectorを使用すると、インデックスでオブジェクトに効率的にアクセスできます。ポインタを格納すると、複数のコピーを保持する必要がなく、オブジェクトのインスタンスを1つだけ使用できます。

5
Thomas Matthews

以前の回答(コメントを参照):myMap.begin();

基本的にペアのベクトルであるベクトルバッキングストアを使用して、ランダムアクセスマップを実装できます。もちろん、その時点で標準ライブラリマップのすべての利点が失われます。

4
Michael Price

まあ、実際にはできません。あなたが見つけた方法は非常に非効率的であり、O(n)(nオペレーション最悪の場合、nはマップ内の要素の数)の計算複雑さを持っています)。

ベクトルまたは配列内のアイテムへのアクセスには、複雑さO(1)(比較計算が一定、単一の演算))があります。

マップは内部で赤黒ツリー(またはavlツリー、実装によって異なります)として実装されており、すべての挿入、削除、およびルックアップ操作はO(log n)の最悪の場合(要素を見つけるために2を基数とする操作で対数が必要)ツリーで)、それはかなり良いです。

あなたが対処できる方法は、ベクターとマップの両方を内部に持つカスタムクラスを使用することです。クラスの最後の挿入は平均O(1)、名前による検索はO(log n)、インデックスによる検索はO(1)ですが、この場合は削除されます操作はO(n)になります。

3

コンテナのような他のマップを使用できます。
サイズフィールドを維持すると、バイナリ検索ツリーのランダムアクセスが容易になります。
これが私の実装です...
stdスタイル、ランダムアクセスイテレータ...
バランスのとれたツリーのサイズ...
https://github.com/mm304321141/zzz_lib/blob/master/sbtree.h
およびB + tree ...
https://github.com/mm304321141/zzz_lib/blob/master/bpptree.h

1
奏之章

std::mapは順序付きコンテナですが、イテレータはランダムアクセスをサポートしていませんが、双方向アクセスをサポートしています。したがって、n番目の要素にアクセスするには、その前のすべての要素をナビゲートする必要があります。あなたの例のより短い代替案は、標準のイテレータライブラリを使用することです:

 std::pair<const std::string, int> &nth_element = *std::next(myMap.begin(), N);

これは線形の複雑さを持ち、大きなマップでこの方法で頻繁にアクセスすることを計画している場合は理想的ではありません。

代替手段は、ランダムアクセスをサポートする順序付けられたコンテナーを使用することです。例えば、 - boost::container::flat_map は、メンバー関数nthを提供します。これにより、探しているものを正確に取得できます。

0
Jorge Bellon