web-dev-qa-db-ja.com

マップで最小値を見つけるにはどうすればよいですか?

mapがあり、マップで最小値(右側)を見つけたいと思います。これが私がそれをした方法です:

bool compare(std::pair<std::string ,int> i, pair<std::string, int> j) {
  return i.second < j.second;
}
////////////////////////////////////////////////////
std::map<std::string, int> mymap;

mymap["key1"] = 50;
mymap["key2"] = 20;
mymap["key3"] = 100;

std::pair<char, int> min = *min_element(mymap.begin(), mymap.end(), compare); 
std::cout << "min " << min.second<< " " << std::endl;

上記のコードは正常に機能し、最小値を取得できます。ただし、このコードを次のようにクラス内に配置すると、機能しないようです。

int MyClass::getMin(std::map<std::string, int> mymap) {
  std::pair<std::string, int> min = *min_element(mymap.begin(), mymap.end(), 
                                                 (*this).compare);
                                                 // Error probably due to "this".
  return min.second; 
}

bool MyClass::compare(
    std::pair<std::string, int> i, std::pair<std::string, int> j) { 
  return i.second < j.second; 
}

クラスでコードを機能させるにはどうすればよいですか?また、追加のcompare関数を記述する必要のないより良い解決策はありますか?

19
Sunny

いくつかのオプションがあります。これを行うための「最良の」方法は、functorを使用することです。これは、呼び出すのが最も速いことが保証されています。

typedef std::pair<std::string, int> MyPairType;
struct CompareSecond
{
    bool operator()(const MyPairType& left, const MyPairType& right) const
    {
        return left.second < right.second;
    }
};



int MyClass::getMin(std::map<std::string, int> mymap) 
{
  std::pair<std::string, int> min 
      = *min_element(mymap.begin(), mymap.end(), CompareSecond());
  return min.second; 
}

CompareSecondクラスをMyClass内にネストすることもできます。

ただし、現在のコードを使用すると、コードを簡単に変更して機能させることができます。関数をstaticにして、正しい構文を使用するだけです。

static bool 
MyClass::compare(std::pair<std::string, int> i, std::pair<std::string, int> j) 
{ 
  return i.second < j.second; 
}

int MyClass::getMin(std::map<std::string, int> mymap) 
{
  std::pair<std::string, int> min = *min_element(mymap.begin(), mymap.end(), 
                                                 &MyClass::compare);
  return min.second; 
}
14
rlbond

C++ 11では、次のことができます。

auto it = min_element(pairs.begin(), pairs.end(),
                      [](decltype(pairs)::value_type& l, decltype(pairs)::value_type& r) -> bool { return l.second < r.second; });

または、次のような素敵な関数に入れます(私はテンプレートの第一人者ではないことに注意してください。これはおそらく多くの点で間違っています):

template<typename T>
typename T::iterator min_map_element(T& m)
{
    return min_element(m.begin(), m.end(), [](typename T::value_type& l, typename T::value_type& r) -> bool { return l.second < r.second; });
}

C++ 14を使用すると、さらに単純化されて次のようになります。

min_element(pairs.begin(), pairs.end(),
            [](const auto& l, const auto& r) { return l.second < r.second; });
13
Timmmm

私は実際に別の質問があります:あなたが定期的に右側の値の最小値を取得する必要がある場合、mapが最良の構造であるよりも確かですか?

同じオブジェクトのセットにインデックスを付ける複数の方法のこれらの問題には、一般にBoost.MultiIndexを使用することをお勧めします...ただし、この「逆マッピング」ビットのみが必要な場合は、Boost.Bimapの方が簡単な場合があります。

このようにして、最小値を探すときに線形検索を行うことはありません:)

2
Matthieu M.

問題はこれです:

bool MyClass::compare

呼び出されるクラスのインスタンスが必要です。つまり、MyClass::compareを呼び出すだけでなく、someInstance.compareが必要です。ただし、min_elementには前者が必要です。

簡単な解決策は、それをstaticにすることです。

static bool MyClass::compare

// ...

min_element(mymap.begin(), mymap.end(), &MyClass::compare);

これにより、インスタンスを呼び出す必要がなくなり、コードに問題がなくなります。ただし、ファンクターを使用してより一般的にすることができます。

struct compare2nd
{
    template <typename T>
    bool operator()(const T& pLhs, const T& pRhs)
    {
        return pLhs.second < pRhs.second;
    }
};

min_element(mymap.begin(), mymap.end(), compare2nd());

これは、各ペアから2番目を取得して取得するだけで、任意のペアで機能します。一般的なものにすることもできますが、それは少し多すぎます。

十分な値で検索する必要がある場合は、Boostの Bimap を使用することをお勧めします。これは双方向マップであるため、キーと値の両方を使用して検索できます。バリューキーマップの前面を取得するだけです。

最後に、マップに入る最小要素をいつでも追跡できます。新しい値を挿入するたびに、それが現在の値よりも小さいかどうかを確認し(おそらく、マップペアへのポインタであるはずであり、nullとして開始します)、小さい場合は、新しい最小値をポイントします。最低値を要求することは、ポインターを逆参照するのと同じくらい簡単になります。

2
GManNickG

C++ 14

Jonathan Geisler's コメント Timmmm's answerC++ 14 で述べたように、 ラムダ関数 パラメーターをauto型指定子。その結果、Timmmmのラムダベースの _min_element_ 行を次のように短縮できます(そして読みやすさが向上します)。

_auto it = std::min_element(std::begin(mymap), std::end(mymap),
                           [](const auto& l, const auto& r) { return l.second < r.second; });
_

注1:この行をMyClass::getMin()関数に入れる場合は、_it->second_を返す必要があります。ただし、空のマップを説明するには、return行を次のように(または同様に)調整する必要があります。

_return it == mymap.end() ? -1 : it->second;
_

注2: Lance Diduck でも言及されているように、getMin()関数へのconst参照によってマップを渡す必要があります。あなたがそれをした方法で、あなたは地図全体の不必要なコピーを作成しています。

Ideoneのコード

0
honk