В принципе, чтобы удалить слово из trie (как оно реализовано в ответе, который вы связали) вы просто должны удалить его _end
маркер, например, как это:
def remove_word(trie, word):
current_dict = trie
for letter in word:
current_dict = current_dict.get(letter, None)
if current_dict is None:
# the trie doesn't contain this word.
break
else:
del current_dict[_end]
Заметим, однако, что это не гарантирует, что Trie имеет минимальный размер. После удаления слова в левой части могут быть ветви, которые больше не используются никакими словами. Это не влияет на правильность структуры данных, это просто означает, что trie может потреблять больше памяти, чем это абсолютно необходимо. Вы можете улучшить это, итерации назад от листового узла и удаления ветвей, пока не найдете тот, у которого есть более одного ребенка.
EDIT: Представьте себе, как можно реализовать функцию удаления, которая также отбирает ненужные ветви. Там, наверное, более эффективный способ сделать это, но это может вам начать:
def remove_word2(trie, word):
current_dict = trie
path = [current_dict]
for letter in word:
current_dict = current_dict.get(letter, None)
path.append(current_dict)
if current_dict is None:
# the trie doesn't contain this word.
break
else:
if not path[-1].get(_end, None):
# the trie doesn't contain this word (but a prefix of it).
return
deleted_branches = []
for current_dict, letter in zip(reversed(path[:-1]), reversed(word)):
if len(current_dict[letter]) <= 1:
deleted_branches.append((current_dict, letter))
else:
break
if len(deleted_branches) > 0:
del deleted_branches[-1][0][deleted_branches[-1][1]]
del path[-1][_end]
По сути, это первый находит «путь» к слову, который собирается быть удален, а затем перебирает, что в обратном направлении, чтобы найти узлы, которые можно удалить. Затем он удаляет корень пути, который может быть удален (который также неявно удаляет узел _end
).
Спасибо, очень хорошая идея! Сейчас у меня проблемы с обратными итерациями. Поскольку вы можете войти в любой dict, чтобы получить ключ/значение, но вы не можете (насколько мне известно) получить «родительский» dict .. Но если вы хотите повторно добавить одно и то же слово, вы просто «re -add "знак _end, так что спасибо! :) –
Да, это довольно сложно, без прямого доступа к «родительскому» dict, см. Мое редактирование, чтобы понять, как вы могли это сделать, не изменяя общую структуру данных. – omz
Отредактировано снова, чтобы сделать его более эффективным. В принципе, нет необходимости удалять все удаленные словари отдельно, этого должно быть достаточно, чтобы просто удалить «корень» найденного пути (все остальные будут дочерними из них). – omz