Самый быстрый способ найти объединение множеств
У меня есть наборы пар int like
set<pair<int,int> > x1, x2, ... xn
(n может быть между 2 и 20). Какой самый быстрый способ найти объединение этих множеств ?
Извините, если я не был ясно в начале, я имел в виду быстро в производительности, выделение памяти не проблема.
7 ответов
к сожалению, я считаю, что вы ограничены в линейную O(N)
решение, так как все объединение было бы комбинацией элементов в обоих наборах.
template<typename S>
S union_sets(const S& s1, const S& s2)
{
S result = s1;
result.insert(s2.cbegin(), s2.cend());
return result;
}
предполагая, что результат тоже должен быть набором, тогда у вас нет выбора, кроме как вставить каждый элемент каждого x_i
в результирующий набор. Таким образом, очевидная реализация:
set<pair<int,int>> x(x1);
x.insert(x2.begin(), x2.end());
// etc
оставшийся вопрос заключается в том, может ли это быть избито для скорости.
один элемент insert
принимает position
намек, что если правильно ускоряет ввод. Так это может оказывается, что что-то вроде этого быстрее, чем x.insert(x2.begin(), x2.end());
:
auto pos = x.begin()
for (auto it = x2.begin(); it != x2.end(); ++it) {
pos = x.insert(pos, *it);
}
это зависит от данных, хотя эта позиция может или не может быть точным. Вы можете убедиться, что это, поставив все элементы в порядке, прежде чем начать, для которого лучший инструмент, вероятно,set_union
. Это лучше назвать merge_and_dedupe_sorted_ranges
, потому что это не имеет ничего общего с std::set
. Вы могли бы либо set_union
в промежуточные векторы или в такие множества:
set<pair<int,int>> x;
set_union(x1.begin(), x1.end(), x2.begin(), x2.end(), inserter(x, x.end());
моя озабоченность использованием set_union
заключается в том, что в чтобы получить преимущество добавления элементов в набор в порядке возрастания, вам нужно создавать новый пустой контейнер каждый раз, когда вы его вызываете (потому что, если он не пуст, то добавленные элементы должны чередоваться со значениями уже в нем). Накладные расходы этих контейнеров могут быть выше, чем накладные расходы на вставку в набор в произвольном порядке: вам придется протестировать его.
сначала найдите объединение наименьших множеств. То есть упорядочите свои наборы по длине набора, вычислите объединение двух наименьших наборов, удалите эти наборы, вставьте объединение в свой список наборов в соответствии с его размером.
Если бы у вас было измерение того, насколько похожи два набора, то вам лучше всего было бы сначала найти объединение наиболее похожих наборов. То есть предпочитают операции объединения, которые устраняют дубликаты раньше.
Edit: и для каждого объединения операция между двумя наборами - объединить меньшие в большие.
Я предполагаю, что с быстро ты имеешь в виду быстро реализовать.
затем: std:: set_union (*)
пример для двух наборов:
#include <set>
#include <algorithm>
#include <iterator>
using namespace std;
int main () {
set<pair<int,int> > a, b, uni;
set_union (a.begin(), a.end(),
b.begin(), b.end(),
inserter(uni, uni.begin()));
}
для n наборов рукописное написание может быть самым доступным решением:
#include <set>
#include <vector>
using namespace std;
int main () {
vector<set<pair<int,int>>> sets;
set<pair<int,int>> uni;
for (const auto &s : sets)
for (const auto &elem : s)
uni.insert (elem);
}
хотя в целом следует отдавать предпочтение стандартным алгоритмам и получать прибыль от их качественной реализации.
Если быстро ты имеешь в виду производительность, мы не можем помочь, как мы у меня нет требований. Различные подходы могут давать разные результаты в различных обстоятельствах.
(*) Примечание: сайт хмурится иногда за то, что не является 100% точным по сравнению со стандартом
можно использовать std:: set_union рекурсивно или просто вставьте все наборы в результирующий набор (повторяющиеся элементы устраняются набором). Если количество элементов очень мало, вы можете попробовать вставить все это в вектор, отсортировать его и использовать std:: unique на вектор.
чтобы сэкономить на выделении памяти и улучшить локальность, было бы лучше использовать один vector<T>
как рабочая память.
построить vector<T>
и зарезервировать общее количество элементов во всех s (подсчет дубликатов). Затем, начиная с пустого диапазона [v.begin(), v.begin())
, расширьте его до набора (уникального, отсортированного) диапазона, добавив содержимое каждого набора, объединяя и uniquifying:
vector<T> v;
v.reserve(<total size>);
for (set<T> &s: sets) {
auto middle = v.insert(v.end(), s.begin(), s.end());
inplace_merge(v.begin(), middle, v.end());
v.erase(v.unique(v.begin(), v.end()), v.end());
}