Как определить различия в двух списках данных
-
02-07-2019 - |
Вопрос
Это упражнение для ребят из CS, чтобы отточить теорию.
Представьте, что у вас есть 2 контейнера с элементами.Папки, URL-адреса, файлы, строки — это действительно не имеет значения.
Каков алгоритм AN для расчета добавленного и удаленного?
Уведомление:Если существует много способов решения этой проблемы, опубликуйте по одному на каждый ответ, чтобы его можно было проанализировать и проголосовать.
Редактировать:Все ответы решают задачу с 4 контейнерами.Можно ли использовать только первые 2?
Решение
Предполагая, что у вас есть два списка уникальных элементов и порядок не имеет значения, вы можете думать о них как о наборах, а не как о списках.
Если вы думаете о диаграмме Венна, где список A — это один круг, а список B — другой, то пересечение этих двух — это постоянный пул.
Удалите все элементы в этом пересечении как из A, так и из B, и все, что осталось в A, будет удалено, а все, что осталось в B, будет добавлено.
Итак, пройдитесь по A, ища каждый элемент в B.Если вы его нашли, удалите его как из A, так и из B.
Тогда A — это список вещей, которые были удалены, а B — это список вещей, которые были добавлены.
Я думаю...
[edit] Хорошо, с новым ограничением «только 2 контейнера» то же самое остается:
foreach( A ) {
if( eleA NOT IN B ) {
DELETED
}
}
foreach( B ) {
if( eleB NOT IN A ) {
ADDED
}
}
Тогда вы не создаете новый список и не уничтожаете старые... но это займет больше времени, как и в предыдущем примере, вы можете просто перебрать более короткий список и удалить элементы из более длинного.Здесь нужно сделать оба списка
Я бы сказал, что мое первое решение не использовало 4 контейнера, оно просто уничтожило два ;-)
Другие советы
Я давно этого не делал, но думаю, что алгоритм выглядит следующим образом...
sort left-list and right-list
adds = {}
deletes = {}
get first right-item from right-list
get first left-item from left-list
while (either list has items)
if left-item < right-item or right-list is empty
add left-item to deletes
get new left-item from left-list
else if left-item > right-item or left-list is empty
add right-item to adds
get new right-item from right-list
else
get new right-item from right-list
get new left-item from left-list
Что касается отношения правого списка к левому списку, удаляет содержит удаленные элементы и добавляет теперь содержит новые элементы.
Что сказал Джо.А если списки слишком велики и не помещаются в памяти, используйте внешнюю утилиту сортировки файлов или сортировку слиянием.
Недостающая информация:Как вы определяете добавленное/удаленное?Например.если списки (A и B) показывают один и тот же каталог на сервере A и сервере B, значит, они синхронизированы.Если я теперь подожду 10 дней, снова создам списки и сравню их, как я смогу определить, было ли что-то удалено?Я не могу.Я могу только сказать, что на сервере А есть файлы, которые не найдены на сервере Б и/или наоборот.Произошло ли это потому, что файл был добавлен на сервер A (поэтому файл не найден на B) или файл был удален на сервере B (поэтому файл не найден на B) больше) — это то, что я не могу определить, просто имея список имен файлов.
В качестве решения, которое я предлагаю, я просто предположу, что у вас есть один список с именем OLD и один список с именем NEW.Все, что было найдено на СТАРОМ, но не на НОВОМ, было удалено.Добавлено все, что есть на НОВОМ, но не на СТАРОМ (например.содержимое одного и того же каталога на одном сервере, однако списки были созданы в разное время).
Далее я буду считать, что дубликатов нет.Это означает, что каждый элемент в любом списке уникален в смысле:Если я сравниваю этот элемент с любым другим элементом в списке (независимо от того, как работает это сравнение), я всегда могу сказать, что этот элемент либо меньше или больше чем тот, с которым я его сравниваю, но никогда не равен.Например.Имея дело со строками, я могу сравнивать их лексикографически, и одна и та же строка никогда не встречается в списке дважды.
В этом случае самым простым (хотя и не обязательно лучшим решением) является:
Отсортируйте СТАРЫЕ списки.Например.если список состоит из строк, отсортируйте их в алфавитном порядке.Сортировка необходима, потому что это означает, что я могу использовать двоичный поиск, чтобы быстро найти объект в списке, предполагая, что он там существует (или чтобы быстро определить, что его вообще не существует в списке).Если список не отсортирован, сложность поиска объекта равна O(n) (мне нужно просмотреть каждый элемент списка).Если список отсортирован, сложность равна всего O(log n), так как после каждой попытки сопоставить элемент в списке я всегда могу исключить 50% несовпадающих элементов в списке.Даже если в списке 100 элементов, поиск элемента (или обнаружение того, что элемента нет в списке) требует не более 7 тестов (или 8?Во всяком случае, гораздо меньше 100). НОВЫЙ список не нужно сортировать.
Теперь мы выполняем исключение списка.Для каждого элемента в НОВОМ списке попытайтесь найти этот элемент в СТАРОМ списке (используя бинарный поиск).Если элемент найден, удалите этот элемент из СТАРОГО списка и также удалите его из НОВОГО списка.Это также означает, что по мере удаления списки становятся меньше, и, следовательно, поиск будет становиться все быстрее и быстрее.Поскольку удаление элемента из списка не влияет на правильный порядок сортировки списков, нет необходимости использовать СТАРЫЙ список на этапе исключения.
В конце исключения оба списка могут оказаться пустыми, и в этом случае они будут равны.Если они не пусты, все элементы, находящиеся в СТАРОМ списке, являются элементами, отсутствующими в НОВОМ списке (в противном случае мы их удалили), следовательно, это элементы удаленные элементы.Все элементы, находящиеся в НОВОМ списке, — это элементы, которых не было в СТАРОМ списке (опять же, в противном случае мы их удалили), поэтому это добавлены элементы.
Являются ли объекты в списке «уникальными»?В этом случае я бы сначала построил две карты (хэш-карты), а затем просмотрел списки и просмотрел каждый объект на картах.
map1
map2
removedElements
addedElements
list1.each |item|
{
map1.add(item)
}
list2.each |item|
{
map2.add(item)
}
list1.each |item|
{
removedElements.add(item) unless map2.contains?(item)
}
list2.each |item|
{
addedElements.add(item) unless map1.contains?(item)
}
Извините за ужасное смешение мета-языков Ruby и Java :-P
В конце концов удаленоЭлементы будет содержать элементы, принадлежащие списку1, но не списку2, и добавленныеЭлементы будет содержать элементы, принадлежащие list2.
Стоимость всей операции составляет O(4*N), поскольку поиск в карте/словаре можно считать постоянным.С другой стороны, линейный/двоичный поиск каждого элемента в списках составит O(N^2).
РЕДАКТИРОВАТЬ:если подумать, переместив последнюю проверку во второй цикл, вы можете удалить один из циклов...но это некрасиво...:)
list1.each |item|
{
map1.add(item)
}
list2.each |item|
{
map2.add(item)
addedElements.add(item) unless map1.contains?(item)
}
list1.each |item|
{
removedElements.add(item) unless map2.contains?(item)
}