Вопрос

У меня есть два vector<MyType*> Объекты называются A а также B. Отказ У класса MyType есть поле ID и я хочу получить MyType* которые внутри A Но не в B. Отказ Я работаю над приложением анализа изображений, и я надеялся найти быстрое / оптимизированное решение.

Это было полезно?

Решение

Неупорядный подход обычно будет иметь квадратичную сложность, если данные заранее не отсортированы (по вашему идентификатору), в этом случае он будет линейным и не потребует повторных поисков через B.

struct CompareId
{
    bool operator()(const MyType* a, const MyType* b) const
    {
        return a>ID < b->ID;
    }
};
...
sort(A.begin(), A.end(), CompareId() );
sort(B.begin(), B.end(), CompareId() );

vector<MyType*> C;
set_difference(A.begin(), A.end(), B.begin(), B.end(), back_inserter(C) );

Другое решение состоит в том, чтобы использовать упорядоченный контейнер, такой как STD ::, набор с использованием, используемым для аргумента шаблона StrictWeakordering. Я думаю, что это будет лучше, если вам нужно применить много настроек операций. У этого есть свой собственный накладные расходы (будучи деревом), но если вы действительно обнаружите, что это будет проблемой эффективности, вы можете реализовать быстрое распределение памяти для вставки и удаления элементов Super Fast (Примечание: сделайте это только, если вы профиль и определите это, чтобы быть узкое место).

Предупреждение: попасть в несколько сложные территории.

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

Это потребует создания карты IDS для счетчиков и требует получения счетчика с карты каждый раз, когда MyType объект создан на основе его идентификатора. Поскольку у вас есть объекты MyType с дублирующими идентификаторами, вам не нужно вставлять на карту так часто, как вы создаете объекты MyType (большинство может просто получить существующий счетчик).

В дополнение к этому, иметь глобальный счетчик «обход», который увеличивается всякий раз, когда он получает.

static unsigned int counter = 0;
unsigned int traversal_counter()
{
    // make this atomic for multithreaded applications and
    // needs to be modified to set all existing ID-associated
    // counters to 0 on overflow (see below)
    return ++counter;
}

Теперь давайте вернемся туда, где у вас есть векторы A и B, хранящиеся в MyType *. Чтобы извлечь элементы в A, которые не находятся в B, мы сначала вызовите Traversal_counter (). Предполагая, что это первый раз, когда мы это называем, это даст нам обходное значение 1.

Теперь итайте через каждый MyType * Object In B и установите общий счетчик для каждого объекта от 0 до обходного значения, 1.

Теперь итайте через каждый mytype * объект в A. Те, которые имеют значение счетчика, которое не соответствует текущему обходу (1), являются элементами в A, которые не содержатся в B.

Что происходит, когда вы переполняете счетчик обхода? В этом случае мы повторяем все счетчики, хранящиеся на карте ID и устанавливают их обратно в нулю вместе с самим счетчиком обхода. Это нужно будет только один раз примерно в 4 миллиардах обходах, если это 32-разрядное unsigned int.

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

Другие советы

Сортировать оба вектора (std::sort) в соответствии с ID, а затем использовать std::set_difference. Отказ Вам нужно будет определить пользовательский компаратор, чтобы пройти как на эти алгоритмы, например,

struct comp
{
    bool operator()(MyType * lhs, MyType * rhs) const
    {
        return lhs->id < rhs->id;
    }
};

Сначала посмотрите на проблему. Вы хотите «все в не в б». Это означает, что вам придется посещать "все в". Вам также придется посетить все в B, чтобы иметь знания о том, что есть и не в B. Так что предполагает, что должен быть O(n) + O(m) решение или принимая свободу, чтобы повысить разницу между n и m, O(2n).

Давайте рассмотрим std::set_difference подход. Каждый вид есть O(n log n), и set_defference O(n). Отказ Таким образом, подход сортировки Sort-set_Difference O(n + 2n log n). Отказ Давайте назовем это O(4n).

Другой подход будет сначала разместить элементы B в набор (или на карте). Итерация через B для создания набора O(n) плюс вставка O(log n) каждого элемента, за которым следует итерация по AO (N), с поиском для каждого элемента A (log n), дает всего: O(2n log n). Отказ Давайте назовем это O(3n), что немного лучше.

Наконец, используя unuported_set (или неупоряжаться_map) и предполагая, что мы получаем средний случай O(1) Вставка и O(1) Просмотр, у нас есть подход, который O(2n). Отказ Ага!

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

Если b Предваривает к A, то пока заполняя a, вы можете бухгалтерское вектор.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top