Предполагается, что наборы хранятся в виде сортированного контейнера (как это делает std :: set).
Существует общий алгоритм объединения двух упорядоченных списков для создания третьего. Идея состоит в том, что когда вы смотрите на главы двух списков, вы можете определить, какая из них ниже, извлечь и добавить ее в хвост вывода, а затем повторить.
Существуют варианты, которые определяют случай, когда две головы равны, и рассматривайте это специально. Примерами этого являются пересечения и союзы.
С установленной асимметричной разницей ключевым моментом является то, что для A-B, когда вы извлекаете головку B, вы отбрасываете ее. Когда вы извлекаете головку A, вы добавляете ее на вход , если голова B не равна, и в этом случае вы также извлекаете это и отбрасываете оба.
Хотя этот подход предназначен для структур данных последовательного доступа (и хранения на магнитной ленте и т. Д.), Иногда очень полезно делать то же самое для структуры данных с произвольным доступом, если в любом случае разумно эффективно получить к ней доступ в любом случае. И вам необязательно извлекать вещи по-настоящему - вы можете делать копирование и шаг вместо этого.
Ключевым моментом является то, что вы последовательно входите в входы, всегда смотря на самое нижнее оставшееся значение, так что (если входы не имеют дубликатов), вы будете сопоставлять элементы. Поэтому вы всегда знаете, является ли ваше следующее нижнее значение для обработки - это элемент из A без соответствия в B и элемент в B без совпадения в A или элемент, равный как в A, так и B.
В целом, алгоритм для заданной разности зависит от представления множества. Например, если набор представлен как бит-вектор, вышеперечисленное будет слишком сложным и медленным - вы просто пропустите векторы, выполняющие побитовые операции. Если набор представлен как хэш-таблица (как в tr1 unordered_set), это неверно, поскольку для него требуются упорядоченные входы.
Если у вас есть собственный двоичный древовидный код, который вы используете для наборов, одним из хороших вариантов является преобразование обоих деревьев в связанные списки, работа над списками, а затем преобразование полученного списка в идеально сбалансированное дерево. Сетка-список связанных списков очень прост, и эти два преобразования можно использовать повторно для других подобных операций.
EDIT
О сложности - с помощью этих заказанных алгоритмов объединения типа О (п) при условии, что вы можете сделать с обходами в заказе в O (N). Преобразование в список и обратно также равно O (n), так как каждый из трех шагов - это O (n) - древовидный список, разность заданий и список для дерева.
Дерево в список в основном делает обход глубины, деконструируя дерево по мере его поступления. Theres трюк для создания этой итерации, хранения «стека» в узлах с частичной обработкой - изменение указателя слева на родительский указатель перед тем, как вы перейдете к левому ребру. Это хорошая идея, если дерево может быть большим и неуравновешенным.
Преобразование списка в дерево в основном предполагает обход глубины воображаемого дерева (основанный на размере, известном с самого начала), строя его по-настоящему, когда вы идете. Например, если дерево имеет 5 узлов, вы можете сказать, что корневой узел будет узлом 3. Вы рекурсивно создаете левое поддерево с двумя узлами, затем возьмите следующий элемент из списка для этого корня, а затем рекурсивно создадите два -node прямое поддерево.
Преобразование из списка в дерево не должно быть реализовано итеративно - рекурсивный результат хорош, так как результат всегда идеально сбалансирован. Если вы не можете обработать глубину рекурсии log n, вы почти наверняка не сможете обработать полное дерево в любом случае.
Нет такой вещи, как C-STL. Вы имеете в виду C++? –
Я знаю. Я просто хотел уточнить, что мне не нужны решения на основе STL. – Aamir
Поскольку STL - это только C++, достаточно сказать, что вы используете C и оставляете это на этом; если бы кто-то ответил на рекомендацию STL, они были бы опущены (и это заслуга). –