java patriciatrie_Patricia前缀树(Patricia Trie)及其基本操作 | 学步园

本文介绍了Patricia Trie(也称为Patricia Trie或Radix Tree),一种压缩的字典树,用于高效地存储和检索共享前缀的字符串。文章通过Java代码展示了Patricia Trie的基本操作,包括插入、删除和查询,详细解释了每个操作的实现逻辑,并提供了测试用例来展示其功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Patricia前缀树(Patricia Trie)及其基本操作

Trie是一种字典树,用于存储文本字符,并利用了单词之间共享前缀的特点,所以叫做前缀树。不像平衡BST,Trie的高度只与最长的文本串的长度s有关系,而与单词的数量n无关。Trie的节点分两种:内部结点和叶子结点,内部结点用来存储单词key的成分字母,如果设字母表大小为d,那么每个内部结点最多有d个孩子,叶子结点存储该单词作为key的数据内容(data)。注意内部结点和叶子结点并不是互斥的,一个内部结点本身可以有儿子结点,同时它也可以是一个叶子结点。例如:

9952465_1309158757PQ6U.gif

这里ab结点本身就是叶子结点,因为它以#结束符标记了,同时它有两个儿子结点a和c,c是叶子结点。

如果一颗Trie中有很多单词只有一个儿子结点,可以用Patricia Trie(Linux内核中叫做Radix Tree)压缩存储。由于#结束符标记被看作是一个叶子结点,那么一颗Patricia Trie的任何内部结点有2个或以上的孩子结点。

Patricia Trie的基本操作包括:插入、删除和查询。插入操作可能会涉及到split(拆分),删除操作可能会涉及到merge(合并)。基于Patricia Trie的基本性质,split和merge操作都是局部的,这样实现起来比较简单。详细内容请参考以下代码:

实现:

import java.util.LinkedList;

import java.util.List;

/**

* Patricia Trie

*

* Copyright (c) 2011 ljs (http://blog.csdn.net/ljsspace/)

* Licensed under GPL (http://www.opensource.org/licenses/gpl-license.php)

*

* @author ljs

* 2011-06-27

*

*/

public class PatriciaTrie {

private class PatriciaTrieNode {

private String key;

private Integer data;

private List children = new LinkedList();

//use "#" for terminal char

private boolean terminal;

public PatriciaTrieNode(){

this.key = "";

}

public PatriciaTrieNode(String key){

this.key = key;

}

public String toString(){

return this.key + (this.terminal?"#":"") + "(" + children.size() +")";

}

}

private PatriciaTrieNode root;

//return the value of the external node if found;

//otherwise, return null

public Integer find(String key){

if(key == null || key.length() == 0)

return null;

if(root==null){

return null;

}else{

return find(root,key);

}

}

private Integer find(PatriciaTrieNode currNode,String key) {

for(int i=0;i

PatriciaTrieNode child = currNode.children.get(i);

//use min(child.key.length, key.length)

int len = child.key.length()

key.length();

int j = 0;

for(;j

if(key.charAt(j) != child.key.charAt(j)){

break;

}

}

if(j==0){//this child doesn't matchany character with the new key

//order keys by lexi-order

if(key.charAt(0)

//e.g. child="e", key="c" (currNode="abc")

// abc

// / /

// e h

return null;

}else{

//e.g. child="e", key="h" (currNode="abc")

continue;

}

}else{//current child's key partially matches with the new key; 0

if(j==len){

if(key.length()==child.key.length()){

if(child.terminal){

//e.g. child="ab", key="ab"

// ab#

// /

// f#

return child.data;

}else{

//e.g. child="ab", key="ab"

// ab

// / /

// e f

return null;

}

}else if(key.length()>child.key.length()){

//e.g. child="ab#", key="abc"

// ab#

// / /

// a c#

String subkey = key.substring(j); //c

//recursion

return find(child,subkey);

}else{ //key.length()

//e.g. child="abc", key="ab"

// abc

// / /

// e f

return null;

}

}else{//0

//e.g. child="abc", key="abd"

// abc

// / /

// e f

return null;

}

}

}

return null;

}

public void delete(String key) throws Exception{

if(key == null || key.length() == 0) return;

if(root==null){

return;

}

delete(root,key);

}

private void delete(PatriciaTrieNode currNode,String key) throws Exception{

boolean done = false;

for(int i=0;i

PatriciaTrieNode child = currNode.children.get(i);

//use min(child.key.length, key.length)

int len = child.key.length()

key.length();

int j = 0;

for(;j

if(key.charAt(j) != child.key.charAt(j)){

break;

}

}

if(j==0){//this child doesn't matchany character with the new key

//order keys by lexi-order

if(key.charAt(0)

//e.g. child="e", key="c" (currNode="abc")

// abc

// / /

// e h

done = true;

throw new Exception("No such key is found for removal!");

}else{

//e.g. child="e", key="h" (currNode="abc")

continue;

}

}else{//current child's key partially matches with the new key; 0

if(j==len){

if(key.length()==child.key.length()){

if(child.terminal){

//found key, delete it

if(child.children.size()==0){

//e.g. child="ab#", key="ab", currNode="a"

// a

// / /

// d ab#

currNode.children.remove(i);

//merge node for currNode

if(!currNode.terminal && currNode.children.size()==1){

PatriciaTrieNode singleChild = currNode.children.get(0); //d

currNode.key += singleChild.key;

currNode.data = singleChild.data;

currNode.terminal = singleChild.terminal;

currNode.children = singleChild.children;

}

}else{ //child.children.size()>=1

//e.g. child="ab#", key="ab", currNode="a"

// a#

// /

// ab#

// /

// f#

child.terminal = false;

//merge node for child

if(child.children.size()==1){

PatriciaTrieNode singleChild = child.children.get(0); //f#

child.key += singleChild.key;

child.data = singleChild.data;

child.terminal = singleChild.terminal; //Note: singleChild may not be external node

child.children = singleChild.children;

}

}

}else{

//e.g. child="ab", key="ab"

// ab

// / /

// e f

throw new Exception("No such key is found for removal!");

}

}else if(key.length()>child.key.length()){

//e.g. child="ab#", key="abc"

// ab#

// / /

// a c#

String subkey = key.substring(j); //c

//recursion

delete(child,subkey);

}else{ //key.length()

//e.g. child="abc", key="ab"

// abc

// / /

// e f

throw new Exception("No such key is found for removal!");

}

}else{//0

//e.g. child="abc", key="abd"

// abc

// / /

// e f

throw new Exception("No such key is found for removal!");

}

done = true;

break;

}

}

if(!done) {

throw new Exception("No such key is found for removal!");

}

}

//value is only located at the external node

private void insert(PatriciaTrieNode currNode,String key,Integer value) throws Exception{

boolean done = false;

for(int i=0;i

PatriciaTrieNode child = currNode.children.get(i);

//use min(child.key.length, key.length)

int len = child.key.length()

key.length();

int j = 0;

for(;j

if(key.charAt(j) != child.key.charAt(j)){

break;

}

}

if(j==0){//this child doesn't matchany character with the new key

//order keys by lexi-order

if(key.charAt(0)

//e.g. child="e" (currNode="abc")

// abc abc

// / / =========> / | /

// e f insert "c" c# e f

PatriciaTrieNode node = new PatriciaTrieNode(key);

currNode.children.add(i,node);

node.terminal = true;

node.data = value;

done = true;

break;

}else{ //key.charAt(0)>child.key.charAt(0)

//don't forget to add the largest new key after iterating all children

continue;

}

}else{//current child's key partially matches with the new key; 0

if(j==len){

if(key.length()==child.key.length()){

if(child.terminal){

throw new Exception("Duplicate Key is found when insertion!");

}else{

//e.g. child="ab"

// ab ab#

// / / =========> / /

// e f insert "ab" e f

child.terminal = true;

child.data = value;

}

}else if(key.length()>child.key.length()){

//e.g. child="ab#"

// ab# ab#

// / / ==========> / | /

// e f insert "abc" c# e f

String subkey = key.substring(j);

//recursion

insert(child,subkey,value);

}else{ //key.length()

//e.g. child="abc#"

// abc# ab#

// / / =========> /

// e f insert "ab" c#

// / /

// e f

String childSubkey = child.key.substring(j); //c

PatriciaTrieNode subChildNode = new PatriciaTrieNode(childSubkey);

subChildNode.terminal = child.terminal;

subChildNode.data = child.data;

subChildNode.children = child.children; //inherited from parent

child.key = key; //ab

child.terminal = true; //ab#

child.data = value;

child.children = new LinkedList();

child.children.add(subChildNode);

}

}else{//0

//e.g. child="abc#"

// abc# ab

// / / ==========> / /

// e f insert "abd" c# d#

// / /

// e f

//split at j

String childSubkey = child.key.substring(j); //c

String subkey = key.substring(j); //d

PatriciaTrieNode subChildNode = new PatriciaTrieNode(childSubkey);

subChildNode.terminal = child.terminal;

subChildNode.data = child.data;

subChildNode.children = child.children; //inherited from parent

//update child's key

child.key = child.key.substring(0,j);

//child is not terminal now due to split, it is inherited by subChildNode

child.terminal = false;

//Note: no need to merge subChildNode

PatriciaTrieNode node = new PatriciaTrieNode(subkey);

node.terminal = true;

node.data = value;

child.children = new LinkedList();

if(subkey.charAt(0)

child.children.add(node);

child.children.add(subChildNode);

}else{

child.children.add(subChildNode);

child.children.add(node);

}

}

done = true;

break;

}

}

if(!done){

PatriciaTrieNode node = new PatriciaTrieNode(key);

node.terminal = true;

node.data = value;

currNode.children.add(node);

}

}

public void insert(String key,Integer value) throws Exception{

if(key == null || key.length() == 0) return;

if(root==null){

root = new PatriciaTrieNode();

}

insert(root,key,value);

}

public PatriciaTrieNode getRoot(){

return root;

}

//for test purpose only

public void printTree(){

this.print(0, this.root);

}

private void print(int level, PatriciaTrieNode node){

for (int i = 0; i < level; i++) {

System.out.format(" ");

}

System.out.format("|");

for (int i = 0; i < level; i++) {

System.out.format("-");

}

if (node.terminal)

System.out.format("%s[%s]#%n", node.key,node.data);

else

System.out.format("%s%n", node.key);

for (PatriciaTrieNode child : node.children) {

print(level + 1, child);

}

}

public void testFind(String key){

Integer val = this.find(key);

if(val != null)

System.out.format("Found key /"%s/" at: %s%n",key,val);

else

System.out.format("Found no such key: /"%s/"%n",key);

}

public static void main(String[] args) throws Exception {

//test insertion

PatriciaTrie ptrie = new PatriciaTrie();

ptrie.insert("ab",1);

ptrie.insert("abc",2);

ptrie.insert("abde",3);

ptrie.insert("abd",4);

//ptrie.insert("dc");

ptrie.insert("dce",5);

ptrie.insert("dceh",6);

ptrie.insert("dceg",7);

ptrie.insert("dca",8);

ptrie.insert("dcf",9);

ptrie.insert("ghk",10);

ptrie.insert("gh",11);

ptrie.insert("mns",12);

ptrie.insert("mnt",13);

ptrie.insert("mn",14);

ptrie.insert("mg",15);

ptrie.printTree();

String key = "dc";

ptrie.testFind(key);

key = "d";

ptrie.testFind(key);

key = "ab";

ptrie.testFind(key);

key = "ef";

ptrie.testFind(key);

key = "zz";

ptrie.testFind(key);

key = "dk";

ptrie.testFind(key);

key = "dcf";

ptrie.testFind(key);

key = "dck";

ptrie.testFind(key);

key = "abd";

ptrie.delete(key);

System.out.format("After delete key: %s%n",key);

ptrie.printTree();

System.out.println("****************************");

ptrie = new PatriciaTrie();

ptrie.insert("bear",1);

ptrie.insert("bell",2);

ptrie.insert("bid",3);

ptrie.insert("bull",4);

ptrie.insert("buy",5);

ptrie.insert("sell",6);

ptrie.insert("stock",7);

ptrie.insert("stop",8);

ptrie.printTree();

System.out.println("****************************");

ptrie = new PatriciaTrie();

ptrie.insert("allot",1);

ptrie.insert("alloy",2);

ptrie.insert("all",3);

ptrie.insert("aloe",4);

ptrie.insert("ant",5);

ptrie.insert("an",6);

ptrie.insert("are",7);

ptrie.insert("ate",8);

ptrie.insert("be",9);

ptrie.printTree();

System.out.println("****************************");

ptrie = new PatriciaTrie();

ptrie.insert("minimize",0);

ptrie.insert("mize",4);

ptrie.insert("ze",6);

ptrie.insert("nimize",2);

ptrie.insert("ize",5);

ptrie.insert("inimize",1);

ptrie.insert("imize",3);

ptrie.insert("e",7);

ptrie.printTree();

key = "ize";

ptrie.testFind(key);

key = "zee";

ptrie.testFind(key);

key = "mize";

ptrie.testFind(key);

key = "mize";

ptrie.delete(key);

System.out.format("After delete key: %s%n",key);

ptrie.printTree();

}

}

测试输出:

|

|-ab[1]#

|--c[2]#

|--d[4]#

|---e[3]#

|-dc

|--a[8]#

|--e[5]#

|---g[7]#

|---h[6]#

|--f[9]#

|-gh[11]#

|--k[10]#

|-m

|--g[15]#

|--n[14]#

|---s[12]#

|---t[13]#

Found no such key: "dc"

Found no such key: "d"

Found key "ab" at: 1

Found no such key: "ef"

Found no such key: "zz"

Found no such key: "dk"

Found key "dcf" at: 9

Found no such key: "dck"

After delete key: abd

|

|-ab[1]#

|--c[2]#

|--de[3]#

|-dc

|--a[8]#

|--e[5]#

|---g[7]#

|---h[6]#

|--f[9]#

|-gh[11]#

|--k[10]#

|-m

|--g[15]#

|--n[14]#

|---s[12]#

|---t[13]#

****************************

|

|-b

|--e

|---ar[1]#

|---ll[2]#

|--id[3]#

|--u

|---ll[4]#

|---y[5]#

|-s

|--ell[6]#

|--to

|---ck[7]#

|---p[8]#

****************************

|

|-a

|--l

|---l[3]#

|----o

|-----t[1]#

|-----y[2]#

|---oe[4]#

|--n[6]#

|---t[5]#

|--re[7]#

|--te[8]#

|-be[9]#

****************************

|

|-e[7]#

|-i

|--mize[3]#

|--nimize[1]#

|--ze[5]#

|-mi

|--nimize[0]#

|--ze[4]#

|-nimize[2]#

|-ze[6]#

Found key "ize" at: 5

Found no such key: "zee"

Found key "mize" at: 4

After delete key: mize

|

|-e[7]#

|-i

|--mize[3]#

|--nimize[1]#

|--ze[5]#

|-minimize[0]#

|-nimize[2]#

|-ze[6]#

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值