Как найти в c ++ STL определенным образом?

0
map< pair<int,int> , int > m ;

Здесь pair.first и pair.second положительны и pair.second >= pair.first.

Я хотел бы найти все итератор /s в map m, что для данного ключа. Ключ представляет собой целое число, которое находится между парами, например, ключ равен 2, а пара - [2,5] и [1,2] и т.д.
например, m[1,3] = 10, m[3,5] = 6, m[1,8] = 9, m[7,8] = 15 то при поиске m.find(3) return iterator для m[1,3], m[1,8], m[3,5] Если ключа нет, он возвращает m.end().

  • 0
    Вы хотите найти ключи, где значение поиска i находится между двумя значениями пары?
  • 0
    @NeilKirk Да.
Показать ещё 7 комментариев
Теги:
algorithm
search
stl
map

4 ответа

1

Я не уверен, почему вы хотите это сделать, но в наши дни библиотека Boost Interval Container довольно способна.

Предполагая, что вы, возможно, хотели бы отслеживать общую (сумму) отображаемых значений для определенной точки, вы могли бы просто применить разделительный стиль разделения к вашим входным данным и прибыли:

Live On Coliru

#include <boost/icl/split_interval_map.hpp>
#include <boost/range/iterator_range.hpp>
#include <iostream>

namespace icl = boost::icl;

int main() {
    using Map  = icl::split_interval_map<int, int>;
    using Ival = Map::interval_type;
    Map m;
    m.add({Ival::closed(1,3), 10});
    m.add({Ival::closed(3,5), 6});
    m.add({Ival::closed(1,8), 9});
    m.add({Ival::closed(7,8), 15});

    for (auto e : m) std::cout << e.first << " -> " << e.second << "\n";

    std::cout << "------------------------------\n";

    for (auto e : boost::make_iterator_range(m.equal_range(Ival::closed(3,3))))
        std::cout << e.first << " -> " << e.second << "\n";
}

Это скажет нам:

[1,3) -> 19
[3,3] -> 25
(3,5] -> 15
(5,7) -> 9
[7,8] -> 24
------------------------------
[3,3] -> 25

Обратите внимание, как

  • консолидация очень точно отражает то, что точка [3,3] является единственной единственной точкой, совпадающей как с данными [1,3] и с [3,5] от входных данных одновременно, и в результате мы получаем полураскрытые интервалы в комбинированный набор ([1,3), [3,3] и (3,5]).
  • Обратите внимание также, как запрос для этой точки правильно возвращает сумму 10+6+9 для всех трех интересующих вас интервалов.

Что такое использование?

Итак, вы видите, что я переключил фокус вопроса на "Как?" к "Что?". Обычно это помогает сформулировать цель кода вместо конкретной механики.

Конечно, если бы вместо суммы, которую вам было бы интересно в среднем, минимуме или максимуме, вы, скорее всего, написали бы какую-то стратегию объединения.

Бонус Если вы захотите, вот как вы можете, по крайней мере, написать решение проблемы, поставленной в OP, используя Boost Icl: Live On Coliru. Хотя он не особенно эффективен, он прямолинейный и надежный.

  • 0
    пример Coliru по-прежнему O(N) , а не O(log N) , или я что-то упустил? (Я нашел похожее решение, которое вы показали, но жаль, что Boost.ICL не будет поддерживать логарифмическое решение).
  • 0
    @TemplateRex Похоже, O (1). Независимо от того, какие аргументы / окружение стандартного ввода / командной строки я передаю программе, для каждого ввода требуется одинаковое количество времени. Я делаю это неправильно?
0

если вам нужен только итератор для следующего значения, найденного на карте, вы можете использовать алгоритм std :: find_if следующим образом:

int key=2;
map<pair<int,int>, int>::iterator it =std::find_if(m.begin(),m.end(),
                         [key](pair<pair<int,int>,int> entry)
                         {
                           return (entry.first.first <= key) 
                                  && (entry.first.second >= key);
                         }
                         );

cout << "the next value is: [" << it->first.first << "/";
cout << it->first.second << "] " << it->second << endl;
0

Нет способа избежать линейного поиска с начала карты, потому что если первый элемент равен {0, INT_MAX}, то он совпадает, а нужные элементы не обязательно находятся в смежном диапазоне, например, если у вас есть {1,3 }, {2,2} {3,5} вам нужен только первый и последний элементы, когда ключ равен 3.

Вы можете остановить поиск, когда достигнете элемента с first большим, чем ключ.

Что-то вроде этого (непроверено):

typedef map< pair<int,int> , int > Map;

std::vector<Map::iterator>
find(int key, const Map& m)
{
  std::vector<Map::iterator> matches;
  for (Map::iterator it = m.begin(), end = m.end(); it != end && key <= it->first; ++it)
  {
    if (it.first >= key && key <= it.second)
      matches.push_back(it);
  }
  return matches;
}

Вы можете превратить его в функтора и использовать find_if но я не уверен, что это того стоит.

Если вы хотите, чтобы один итератор возвращался за звонок:

typedef map< pair<int,int> , int > Map;

Map::iterator
find(int key, const Map& m, Map::iterator it)
{
  for (Map::iterator end = m.end(); it != end && key <= it->first; ++it)
  {
    if (it.first >= key && key <= it.second)
      return it;
  }
  return m.end();
}

Map::iterator
find(int key, const Map& m)
{ return find(key, m, m.begin()); }
  • 0
    Мне нужна log (N) временная сложность, аналогичная кнопкам Print BST в заданном диапазоне.
  • 0
    Вы не можете получить журнал (N). Вы должны начать поиск с первого элемента, и вы должны проверить каждый элемент до m.lower_bound({key+1,0})
0

Я думаю, что вместо итераторов вы могли бы хранить (и использовать) соответствующие ключи карты. Если это так, то код может выглядеть так:

#include <iostream>
#include <map>
#include <algorithm>
#include <vector>
#include <utility>

int main() 
{
    std::map<std::pair<int, int>, int> m;
    m[{ 1, 3}] = 10; 
    m[{ 3, 5}] = 6;
    m[{ 7, 8}] = 15;

    typedef std::map<std::pair<int, int>, int>::value_type value_type;
    typedef std::map<std::pair<int, int>, int>::key_type key_type;

    int search;

    auto in_range = [&search]( const value_type &value )
    {
        return value.first.first <= search && search <= value.first.second; 
    };

    search = 3;

    std::vector<key_type> v;
    v.reserve( std::count_if( m.begin(), m.end(), in_range ) );

    for ( const auto &p : m )
    {
        if ( in_range( p ) ) v.push_back( p.first );
    }

    for ( const auto &p : v ) 
    {
        std::cout << "[ " << p.first << ", " << p.second << " ]" << std::endl;
    }

    return 0;
}

Выход

[ 1, 3 ]
[ 3, 5 ]

Учтите, что предполагается, что key.first меньше или равен ключу. Второй, где ключ является ключом к карте.

  • 0
    Это работает, но делает два полных прохода по карте, даже если у каждого элемента есть p.first > key
  • 0
    @Jonathan Wakely В этом случае сначала вы можете использовать upper_bound, чтобы найти первый подходящий итератор.
Показать ещё 2 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню