Предполагая, что совпадения префиксов желательны, я бы предложил использовать три Патриции. Предполагая, что имя и номер телефона никогда не могут совпадать - т.е. у вас не будет никого с именем 435-9876 в вашем каталоге - тогда вы можете вставить пары с номером телефона, индексируемым с помощью дерева, а также пары, проиндексированные по имени. Если по какой-то странной причине возможны коллизии имен и имен, вы можете добавить специальный символ в префикс телефонных номеров, чтобы они могли дольше сталкиваться.
Вставки будут стоить O (s)
Поиски будут стоить O (s)
где s - длина строки поиска / вставленного ключа.
Это решение также позволит вам поддерживать автозаполнение в стиле оболочки Linux, где вы можете определить количество записей, которые соответствуют определенному имени, как его набрано, или даже отобразить все совпадения по требованию.
EDIT:
Патриция-дерево похожа на обычное дерево, за исключением того, что ветви в патриции-дерево появляются только тогда, когда есть по крайней мере два разных ключа с общим префиксом, которые не были разделены предыдущей ветвью.
- Внутренние узлы указывают индекс, по которому ключи различаются, а конечные узлы хранят фактические ключи.
- Все дочерние узлы имеют общий префикс родителя.
- Ветви от узла «помечены» символом, который находится в положении, указанном родителем.
- Ребенок не может указать меньший индекс, чем его родитель.
Таким образом, если Jane и Jamie являются единственными ключами, хранящимися в дереве, у него есть три узла - родительский элемент представляет общий префикс «Ja», одна ветвь ведет к поддереву, содержащему все строки, начинающиеся с «Jan», - и так как в листовом узле «Jane» хранится только один, в то время как другая ветвь ведет к поддереву, содержащему все строки, начинающиеся с «Jam», опять же, при этом они являются единственными.
[2]
n m
'Jane' 'Jamie'
Если вы добавите Джеймса, вы получите
[2]
n m
'Jane' [3]
e i
'James' 'Jamie'
А потом добавляем Джамелию
[2]
n m
'Jane' [3]
e i
[4] 'Jamie'
l s
'Jamelia' 'James'
Поиск прост. Начиная с корня, указанный индекс проверяется. Если ни один из потомков не помечен с указанным фактическим значением, в нашем примере, например, если ищется Джаспер, так как символ в позиции 2 - это s, а не n или m, поиск возвращает, что значение не существует. В противном случае выполняется соответствующая ветвь, пока не будет найдено значение, не будет доказано, что его нет, или останется несколько совпадений. Например. поиск Jam останавливается на узле с меткой [3]. Есть несколько совпадающих узлов, но ключа Jam нет. В этом случае вы можете пройти по поддереву с корнем в [3], чтобы создать список частичных совпадений. Обратите внимание, что это применимо только в том случае, если вы выполняете частичное совпадение, если указан весь ключ, результат поиска 'Jam' не будет совпадать.
Стоимость поиска в порядке длины ключа, s , потому что, как вы можете видеть, существует не более s уровней до того, как ключ найден или показан не быть там. Точно так же стоимость вставки также находится в порядке длины ключа, который будет вставлен, поскольку вставка выполняется путем поиска, а затем добавления ветвления узла по самому длинному префиксу в дереве.
Вы можете найти реализацию Java здесь и то, что выглядит реализацией C ++ здесь .
На самом деле я никогда не использовал ни одну из реализаций, на которые я вам указывал, поэтому не могу ручаться за их правдивость. Если вы решите реализовать структуру данных самостоятельно, я бы посоветовал вам использовать двоичный алфавит, так будет немного проще! Здесь - статья, описывающая алгоритм.