Trie (Prefix Tree) в Python

Я не знаю, стоит ли спрашивать об алгоритмах. Но посмотрим, получаю ли я ответы...:)

Если что-то неясно, я очень рад прояснить ситуацию.

Я только что реализовал Trie в python. Однако один бит казался более сложным, чем следовало бы (как человек, который любит простоту). Возможно, у кого-то была аналогичная проблема?

Моя цель заключалась в том, чтобы свести к минимуму количество узлов, сохранив в своем корне самый большой общий префикс субтри. Например, если бы у нас были слова stackoverflow, stackbase и на основе стека, тогда дерево выглядело бы примерно так:

              [s]tack
[o]verflow ______/ \_______ [b]ase
                                  \___ [d]

Обратите внимание, что все еще можно думать о ребрах, имеющих один символ (первый из дочерних элементов node).

Найти-запрос прост в реализации. Вставка не сложна, но несколько сложнее, чем я хочу.: (

Моя идея заключалась в том, чтобы вставлять ключи один за другим (начиная с пустого trie), сначала ищем вставленный ключ k (Find (k)), а затем переставляя/разбивая узлы локально на место, где процедура поиска останавливается. Там оказалось 4 случая: (Пусть k - ключ, который мы хотим вставить, а k '- ключ node, где поиск закончился)

  • k совпадает с k '
  • k - "правильный" префикс k '
  • k 'является "правильным" префиксом k
  • k и k 'используют общий префикс, но ни один из случаев (1), (2) или (3) не встречается.

Кажется, что каждый из случаев уникален и, следовательно, подразумевает различные модификации Trie. НО: это действительно так сложно? Я что-то упускаю? Есть ли лучший подход?

Спасибо:)

Ответ 1

С первого взгляда кажется, что вы внедрили Patricia Trie. Этот подход также называется сжатием путей в некоторой литературе. Должны быть копии этой бумаги, которые не находятся за платой ACM, которая будет включать алгоритм вставки.

Также есть другой метод сжатия, который вы можете посмотреть: сжатие уровня. Идея сжатия пути заключается в замене строк одиночных дочерних узлов на один супер node, который имеет счетчик "пропуска". Идея сжатия уровня заключается в замене полных или почти полных поддеревьев супер-w630 со степенью "градуса", которая говорит, сколько цифр ключа node декодирует. Там также третий подход, называемый сжатием ширины, но я боюсь, что моя память не справляется со мной, и я не смог найти ее описание с быстрым поиском.

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

Ответ 2

Я не вижу ничего плохого в вашем подходе. Если вы ищете решение по шипу, возможно, действие, предпринятое в случае 4, действительно возможно для первых трех случаев, IE найдет общий префикс k и k' и перестроит node с учетом этого. Если это случается, что ключи были префиксами друг друга, получившееся trie будет по-прежнему правильным, только реализация сделала немного больше работы, чем это было на самом деле. но опять же, без какого-либо кода, чтобы смотреть на это трудно сказать, если это работает в вашем случае.

Ответ 3

Отчасти касательная, но если вас сильно беспокоит количество узлов в вашей Trie, вы можете посмотреть на присоединение к своим суффиксам. Я бы посмотрел на диаграмму DAWG (Directed Acyclic Word Graph): http://en.wikipedia.org/wiki/Directed_acyclic_word_graph

Недостатком является то, что они не очень динамичны, а их создание может быть затруднено. Но если ваш словарь статичен, они могут быть суперкомпактными.

Ответ 4

У меня есть вопрос относительно вашей реализации. Каков уровень детализации, который вы решили разделить на свои строки, чтобы создать дерево префикса. Вы можете разделить стек как на s, t, a, c, k, так и на st, ta, ac, ck и многие другие ngrams. В большинстве реализаций дерева префикса учитывается алфавит для языка, основанный на этом алфавите, вы делаете расщепление.

Если вы строили реализацию дерева префикса для python, тогда ваши алфавиты были бы такими, как def,:, if, else... etc

Выбор правильного алфавита делает огромную разницу в построении эффективных деревьев префикса. Что касается ваших ответов, вы можете искать пакеты PERL на CPAN, которые выполняют самую длинную общую вычисление подстроки с помощью trie. Возможно, вам повезло, так как большая часть их реализации довольно надежна.