Что такое Алгоритм или код для получения порядкового номера элемента в списке, отсортированном по значению в c ++ - PullRequest
3 голосов
/ 19 ноября 2009

Это похоже на недавний вопрос .

Я буду поддерживать отсортированный список значений. Я буду вставлять элементы произвольного значения в список. Каждый раз, когда я вставляю значение, я хочу определить его порядковый номер в списке (1-й, 2-й, 1000-й). Какова наиболее эффективная структура данных и алгоритм для этого? Очевидно, есть много алгоритмов, которые могут позволить вам сделать это, но я не вижу способа легко сделать это, используя простую функциональность шаблона STL или QT. В идеале я хотел бы знать о существующих библиотеках C ++ с открытым исходным кодом или образце кода, который может это сделать.

Я могу представить, как модифицировать B-дерево или аналогичный алгоритм для этой цели, но, похоже, должен быть более простой способ.

Edit3:

Майк Сеймур довольно хорошо подтвердил, что, как я писал в своем первоначальном посте, действительно нет способа выполнить эту задачу, используя простой STL. Поэтому я ищу хороший шаблон c ++ с открытым исходным кодом, сбалансированное дерево или аналогичный, который можно выполнить без изменения или с наименьшим возможным изменением - Павел Швед показал, что это возможно, но я бы предпочел не погружаться в реализацию сбалансированного дерева себя.

(история должна показать мои неудачные попытки изменить код Матье, чтобы он стал O (log N) с помощью make_heap)

Редактировать 4:

Я все еще отдаю должное Павлу за то, что он указал, что btree может дать решение для этого, я должен упомянуть тот самый простой способ достижения такого рода функциональности без реализации custom Собственный шаблон btree c ++ должен использовать базу данных в памяти . Это даст вам log n, и его будет довольно легко реализовать.

Ответы [ 8 ]

7 голосов
/ 19 ноября 2009

Двоичное дерево хорошо с этим. Его модификация также проста: просто сохраните в каждом узле количество узлов в его поддереве.

После того, как вы вставили узел, снова выполните его поиск, пройдя от корня к этому узлу. И рекурсивно обновлять индекс:

if (traverse to left subtree)
  index = index_on_previous_stage;
if (traverse to right subtree)
  index = index_on_previous_stage + left_subtree_size + 1;
if (found)
  return index + left_subtree_size;

Это займет время O (log N), как при вставке.

5 голосов
/ 19 ноября 2009

Я думаю, что вы можете std::set здесь. Он обеспечивает сортировку, а также возвращает позицию итератора, в который вставлено значение. С этой позиции вы можете получить индекс. Например:

std::set<int> s;
std::pair<std::set<int>::iterator, bool> aPair = s.insert(5);
size_t index = std::distance(s.begin(), aPair.first) ;
1 голос
/ 19 ноября 2009

Если вам нужна порядковая позиция, вам нужен контейнер, который моделирует концепцию RandomAccessContainer ... в основном, std::vector.

Операции сортировки на std::vector относительно быстрые, и вы можете добраться до желаемой позиции, используя std::lower_bound или std::upper_bound, вы можете самостоятельно решить, хотите ли вы использовать несколько значений одновременно, чтобы получить все равные хорошим способом является использование std::equal_range, которое в основном дает тот же результат, что и применение границ lower и upper, но с большей сложностью.

Теперь, для порядкового положения, большие новости в том, что std::distance как сложность O (1) на моделях RandomAccessIterator.

typedef std::vector<int> ints_t;
typedef ints_t::iterator iterator;

ints_t myInts;

for (iterator it = another.begin(), end = another.end(); it != end; ++it)
{
  int myValue = *it;
  iterator search = std::lower_bound(myInts.begin(), myInts.end(), myValue);
  myInts.insert(search, myValue);
  std::cout << "Inserted " << myValue << " at "
            << std::distance(myInts.begin(), search) << "\n";
  // Not necessary to flush there, that would slow things down
}


// Find all values equal to 50
std::pair<iterator,iterator> myPair =
    std::equal_range(myInts.begin(), myInts.end(), 50);
std::cout << "There are " << std::distance(myPair.first,myPair.second)
          << " values '50' in the vector, starting at index "
          << std::distance(myInts.begin(), myPair.first) << std::endl;

Легко, не правда ли?

std::lower_bound, std::upper_bound и std::equal_range имеют сложность O (log (n)), а std::distance имеет сложность O (1), поэтому все там достаточно эффективно ...

РЕДАКТИРОВАТЬ : как подчеркнуто в комментариях >> вставка на самом деле O (n), так как вы должны перемещать элементы вокруг.

1 голос
/ 19 ноября 2009

Если, как вы говорите в одном из ваших комментариев, вам нужна только приблизительная порядковая позиция, Вы можете оценить это по диапазону значений, который у вас уже есть - вам нужно только прочитать первое и последнее значения в коллекции за постоянное время, примерно так:

multiset<int> values;

values.insert(value);
int ordinal = values.size() * (value - values.front()) /
                              (values.back()-values.front());

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

class SortedValues : public multiset<int>
{
public:
    SortedValues() : sum(0), sum2(0) {}

    int insert(int value)
    {
        // Insert the value and update the running totals
        multiset<int>::insert(value);
        sum += value;
        sum2 += value*value;

        // Calculate the mean and deviation.
        const float mean = float(sum) / size();
        const float deviation = sqrt(mean*mean - float(sum2)/size());

        // This function is left as an exercise for the reader.
        return size() * EstimatePercentile(value, mean, deviation);
    }

private:
    int sum;
    int sum2;
};
1 голос
/ 19 ноября 2009

Обратите внимание, что функция-член std :: list insert (it, value) возвращает итератор для вновь вставленного элемента. Может быть, это может помочь?

0 голосов
/ 19 ноября 2009

Если у вас есть итератор для элемента (в соответствии с рекомендациями dtrosset), вы можете использовать std :: distance (например, std :: distance (my_list.begin (), item_it))

0 голосов
/ 19 ноября 2009

если у вас есть итератор, для которого вы хотите найти индекс, используйте std :: distance, это либо O (1), либо O (n) в зависимости от контейнера, однако контейнеры O (1) будут иметь вставки O (n), поэтому в целом вы смотрите на алгоритм O (n) для любого контейнера stl.

как уже говорили другие, не сразу понятно, почему это полезно?

0 голосов
/ 19 ноября 2009

Зачем вам нужна порядковая позиция? Как только вы вставите другой элемент в список, порядковые позиции других элементов в списке изменится, поэтому, похоже, нет смысла находить порядковый номер при выполнении вставки.

Может быть, лучше просто добавить элементы в вектор, отсортировать, а затем использовать бинарный поиск, чтобы найти порядковый номер, но это зависит от того, чего вы на самом деле пытаетесь достичь

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...