`
bachmozart
  • 浏览: 111382 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

双数组Trie树实现笔记

阅读更多
双数组的算法可以参照 、
An Efficient Implementation of Trie Structures
地址:http://www.cs.ubc.ca/local/reading/proceedings/spe91-95/spe/vol22/issue9/spe777ja.pdf
该算法实际描述的是一个reduced trie
上面讲的已经很详细了,只有插入操作的冲突解决第4个case 稍微麻烦些,简单记录一下

在插入了 bachelor,jar,badge 三个单词后形成的trie树如图


这时有待插入单词 baby,那么根据定义,我们进行状态的转移

BASE [l]+‘b’= BASE [ l]+3=l+3=4, and CHECK [4]=1
BASE [4]+‘a’= BASE [4] +2=l+2=3, and CHECK [3]=4
BASE [3]+‘b’= BASE [3 ]+3=l+3=4, and CHECK [4]=l != 3

冲突发生,解决冲突的办法有2个

a. 改变base[1]的值,使节点4变为 available状态,这样我们就可以指定 check[4]=3
b. 改变base[3]的值,使base[3] + 'b'=t, and check[t]=0

a,b具体选择哪个取决于 节点1,和节点3 谁的child更多,出于性能的考虑,我们当然改变那个child较少的节点,注意,节点3要算上新字符'b',所以 节点1有 [b,j] 2个child,节点3有[c,d,b] 3个child
所以我们执行选择 a,改变 base[1]的值,最终结果如图

具体base[1]的值选取,是从1开始,使 base[1] + base[1]的任意child 都可用,即
check[val + each child of base1]==0

本例base[1]最终选取值 为 4
base[1]+'b' = 4+'b'= 4+3 =7   ,check[7]=0 available      完成 1 -> 7 节点关系
base[1]+'j' = 4+ 'j'=4+11=15  ,check[15]=0 available

这样 节点1的child就变为 7,15,从而使原来的 4,12变为空闲节点,通过以下步骤

base[7]=base[4]   这样 节点7 + 'a' 能正确得到下一节点 3    完成 7 -> 3 节点关系
check[7]=check[4] 这样 使得节点7可以指向(验证)前一节点是 1  完成 7 -> 1 节点关系

以上步骤分别完成了 1 -> 7,7 -> 1,7 -> 3 的关系建立

可以看出我们还差一步就是 建立  3 ->7 的节点关系建立

也就是说要使原来被替换掉的老节点的所有child都,改叫新的节点为父亲
即使原来所有 check[?]=4 的 都变成 check[?]=7, (4是老节点,3的原来的父亲,7是新节点,现在3的新父亲)

以上步骤完成后,我们成功的使节点 4 变为空闲节点,冲突解决,完整代码如下:

#define BASE(n)  (2*n >= bc_len ? 0 : bc[2*n])
#define CHECK(n) (2*n+1 >=bc_len ? 0 : bc[2*n+1])
#define ENCODE(n) ((unsigned char)n-95)
#define MIN_CHILD 97
#define MAX_CHILD 255

//forward declaration
static int bc_len;
static int tail_len;
static int tail_pos;
//base and check,the double array
static int *bc;
//the tail array
static unsigned char *tail;

void bc_init();
int bc_go_through(char *words,int insert);
static int bc_change(int current,int s,char *list,char c);
static char *list_childs(int node);
static void write_tail(char *words,int tail_pos);
static void separate(int s,char *words,int tail_pos);
static int x_check(char *list);
static void tail_insert(int s,char *temp,char *words);
static void read_tail(char *temp,int pos);
static void bc_insert(int s,char *words);
static void bc_realloc();
static void w_base(int n,int value);
static void w_check(int n,int value);

static void bc_realloc(){
	int *new;
	new=(int *)realloc(bc,sizeof(*new)*bc_len*2);
	if(!new){
		exit(1);
	}
	bc_len*=2;
	bc=new;
}
void bc_init(){
	bc_len=1024;
	tail_len=1024;
	bc=(int *)malloc(sizeof(*bc) * bc_len);
	if(!bc) exit(1);

	tail=(unsigned char *)malloc(sizeof(*tail)*tail_len);
	if(!tail) exit(1);

	w_base(1,1);
	tail_pos=1;
}
int bc_go_through(char *words,int insert){
	int i=0,s=1,t,len;
	char c,temp[100];
	words=strcat(words,"#");
	len=strlen(words);
	for(i=0;i<len;i++){

		if(words[i]=='#') break;

		c=ENCODE(words[i]);
		t=BASE(s)+c;
		if(CHECK(t)!=s){
			//case 1: check[t]==0,no collision,just make check[t]=s and save the rest in tail
			//case 2: collision occurs,it's the type 4 collision,described in "An Efficient Implements Of Trie Structures"
			if(insert) bc_insert(s,words+i);
			return 0;
		}
		if(BASE(t)<0) break; //case 1: found it,case 2: the rest in the tail and the rest of the words is different
		s=t;
	}
	//read tail and compare it with the rest of given words,then decide what to do next
	if(BASE(t)<0){
		read_tail(temp,BASE(t));
		if(!strcmp(temp,words+i+1)){
			return 1;
		}else{
			if(insert) tail_insert(t,temp,words+i+1); //type 3 collision
			return 0;
		}

	}	
	return 0;
}
static void tail_insert(int s,char *temp,char *words){
	//solve type 3 collision
	char list[3];
	int old_tail_pos,len=0,i,t;
	old_tail_pos=abs(BASE(s));	
	while(temp[len]==words[len]) len++;
	for(i=0;i<len;i++){
		list[0]=temp[i];
		list[1]='\0';
		
		w_base(s,x_check(list));
		t=BASE(s)+ENCODE(temp[i]);
		w_check(t,s);
		s=t;
	}
	list[0]=temp[len];
	list[1]=words[len];
	list[2]='\0';

	w_base(s,x_check(list));
	separate(s,temp+len,old_tail_pos);
	separate(s,words+len,tail_pos);
	tail_pos+=strlen(words+len);
}
static void separate(int s,char *words,int tail_pos){
	int t;
	t=BASE(s)+ENCODE(words[0]);
	w_check(t,s);
	w_base(t,(-1)*tail_pos);
	words++;
	write_tail(words,tail_pos);
}
static void write_tail(char *words,int tail_pos){
	unsigned char *new;
	int len;
	len=strlen(words);
	if(tail_pos+len>=tail_len){
		new=(unsigned char *)realloc(tail,sizeof(*new)*tail_len*2);
		if(!new) exit(1);
		tail_len*=2;
		tail=new;
	}
	memcpy(tail+tail_pos,words,len);
}
//choose a val,which make check[val + each element in list] == 0, actually val is base[n]
static int x_check(char *list){
	int val=1,i=0,t;
	while(list[i]!='\0'){
		t=val+ENCODE(list[i++]);
		if(CHECK(t)!=0){
			val++;
			i=0;
		}
	}
	return val;
}
static void read_tail(char *temp,int pos){
	int i=0;
	pos=abs(pos);
	while(tail[pos+i] != '#') i++;
	memcpy(temp,tail+pos,i+1);
	temp[i+1]='\0';
}
static void bc_insert(int s,char *words){
	int t;
	char *s_childs,*t_childs;
	t=BASE(s)+ENCODE(words[0]);
	if(CHECK(t)!=0){
		s_childs=list_childs(s);
		t_childs=list_childs(CHECK(t));

		strlen(s_childs)+1>strlen(t_childs) ? bc_change(s,CHECK(t),t_childs,'\0') : bc_change(s,s,s_childs,words[0]);
	}
	separate(s,words,tail_pos);
	tail_pos+=(strlen(words)-1);
}
static int bc_change(int current,int s,char *list,char c){
	int old_base,new_base,i=0,j=0,new_node,old_node,len;
	char *old_childs;
	old_base=BASE(s);
	if(c!='\0'){
		len=strlen(list);
		list[len]=c;
		list[len+1]='\0';
	} 
	new_base=x_check(list);
	w_base(s,new_base);
	while(list[i]!='\0'){
		new_node=new_base+ENCODE(list[i]);
		old_node=old_base+ENCODE(list[i]);

		w_base(new_node,BASE(old_node));
		w_check(new_node,s);

		if(BASE(old_node)>0){
			old_childs=list_childs(old_node);
			while(old_childs[j]!='\0'){
				w_check(BASE(new_node)+ENCODE(old_childs[j]),new_node);
				j++;
			}
		}
		w_base(old_node,0);
		w_check(old_node,0);
		i++;
	}
	return current;
}
static char *list_childs(int node){
	char *childs;
	int i,c,t;
	childs=(char *)malloc(sizeof(*childs)*(MAX_CHILD-MIN_CHILD+1));
	for(i=0,c=MIN_CHILD;c<MAX_CHILD;c++){
		t=BASE(node)+ENCODE(c);
		if(CHECK(t)==node) childs[i++]=c;
	}
	childs[i]='\0';
	return childs;
}
static void w_base(int n,int value){
	if(n>bc_len) bc_realloc();
	bc[2*n]=value;
}
static void w_check(int n,int value){
	if(n>bc_len) bc_realloc();
	bc[2*n+1]=value;
}

为了和文档中的例子保持一致方便调试,定义了这个宏 ENCODE(n) ((unsigned char)n-95)
以及 MIN_CHILD,MAX_CHILD
根据实际需要,需要改变这个宏的定义,调用实际的编码函数
程序基本和文档中的保持一致,修改了一些小的地方

写得太乱了,有兴趣的主要还是看文档吧,后续我也写个文字过滤试试
目前还没想明白处理中英文时,实际的编码是否可以直接按ascii码处理,还要再想想
  • 大小: 46.2 KB
  • 大小: 33.7 KB
  • 大小: 8.4 KB
  • 大小: 7.9 KB
分享到:
评论
1 楼 sdh5724 2009-05-23  
C的实现有一个open source的. 我记得还是linux下可安装的开发包呢. 不过看过代码, 有BUG.

最近玩这个的人很多呀.

相关推荐

    双数组Trie优化算法及其应用研究

    **双数组Trie树(Double-Array Trie):** 是一种结合了数组和Trie树优点的数据结构。它由两个数组组成,一个称为基数数组(base array),另一个称为检查数组(check array)。基数数组用于存储指向字符的位置信息,而检查...

    双数组Trie树算法优化及其应用研究.pdf

    ### 双数组Trie树算法优化及其应用研究 #### 摘要与关键词解析 本文主要探讨了一种针对双数组Trie树(Double-Array Trie)算法的优化策略,并通过实验验证了该策略的有效性。双数组Trie树是一种高效的数据结构,常...

    基于双数组Trie_树中文分词研究

    ### 基于双数组Trie树中文分词研究 #### 概述 本文献针对中文信息处理中的分词问题,研究了一种基于双数组Trie树(Double-Array Trie, DAT)的优化方法。传统的分词算法面临着空间利用率低、插入速度慢等问题,...

    java数组-基于java实现的双数组Trie树.zip

    双数组Trie树(Double Array Trie)是Trie树的一种优化实现,由日本的Hideo Bannai和Ichiro Hamana在1992年提出。它通过使用两个数组(一个用于表示字符位置,另一个用于表示子节点位置)来存储树的信息,从而节省...

    DoubleArrayTrie(双数组Trie树)

    **DoubleArrayTrie(双数组Trie树)详解** DoubleArrayTrie(简称DAT),是一种高效的数据结构,常用于字符串的查找和匹配,特别是在分词检索、数据挖掘以及搜索引擎等领域有着广泛的应用。它是由日本学者高津陵...

    基于双数组Trie树中文分词研究_赵欢 (1)1

    通过这些优化,他们实现了基于双数组Trie树的中文分词系统,并与其他分词方法进行了比较。结果显示,优化后的双数组Trie树在插入速度、空间利用率和分词查询效率上都有显著提升。 中文信息处理中的分词是一个关键...

    双数组 DoubleArray Trie树的数组实现 双数组字典

    双数组Trie(Double-ArrayTrie)是trie树的一个简单而有效的实现,由两个整数数组构成,一个是base[],另一个是check[]。设数组下标为i,如果base[i],check[i]均为0,表示该位置为空。如果base[i]为负值,表示该状态为...

    基于双数组树Trie的词典查询算法

    双数组树Trie,也称为Double-Array Trie,是一种高效的词典查询算法,它解决了传统Trie树在空间效率上的问题。在汉语信息处理系统中,词典查询扮演着至关重要的角色,因为需要频繁地访问词典以获取词汇信息。传统的...

    双数组 Trie源码

    双数组 Trie(Double-Array Trie,DART)是 Trie 结构的一种优化实现,由 Hitachi 的 Hideo Bannai 和 Naoki Kanazawa 在1996年提出。DART 提供了快速的插入、删除和查找操作,特别适合于大量字符串数据的处理。 **...

    基于双数组Trie树中文分词研究* (2009年)

    对双数组Trie树(Double-Array Trie)分词算法进行了优化:在采用Trie树构造双数组Trie树的过程中,优先处理分支节点多的结点,以减少冲突;构造一个空状态序列;将冲突的结点放入Hash表中,不需要重新分配结点.然后...

    用Trie树实现词频统计和单词查询

    一个简单的C语言程序:用Trie树实现词频统计和单词查询

    CQ V2.0分词bates(基于双数组tire树)

    与传统的Trie树相比,双数组Trie树通过将节点信息分散到两个数组中,实现了空间和时间上的优化。这两个数组分别是字符数组(Character Array, C-array)和检查数组(Check Array, A-array),它们共同维护了字符串的...

    Trie 树实现的源码

    ### Trie树实现详解 #### 一、Trie树简介 Trie树,也称为前缀树或字典树,是一种用于存储字符串数据结构。它利用字符串间的公共前缀来减少所需的存储空间,使得查找字符串更加高效。Trie树在很多应用中都有广泛的...

    DoubleArrayTrie:双端trie树的python实现

    DoubleArrayTrie 双端trie树的python实现 版本翻译于 将其改写成python3.5版本 ...在存储值很多且多有冲突、字符编码范围较大的情况下,双数组Trie树很可能在序列化到硬盘以及加载到内存的占用空间都远大于字典Trie树

    网络安全态势感知中Trie树关键词高速匹配算法研究.pdf

    文章中提及的双数组Trie树是一种特别的Trie树结构,它使用了两个数组来存储Trie树的节点信息,包括节点的索引和转移函数。这种结构有助于提高检索效率,因为检索操作通常可以在常数时间内完成。但与此同时,双数组...

    双数组辞典生成程序

    双数组Trie(Double-Array Trie),也称为Darts,是Trie数据结构的一种优化实现。Trie,又称“前缀树”或“字典树”,是一种用于存储动态集合或关联数组的搜索树,其中每个节点代表一个字符串的前缀。双数组Trie的...

    IT笔试面试--Trie树前缀树常考题目及解析

    这是一种高效的Trie树实现方式,通过两个数组来存储节点信息,从而大幅度减少了存储空间的需求。双数组结构包括一个基本数组和一个检查数组,基本数组用来存储节点信息,检查数组用来提高空间利用率和访问速度。 ##...

Global site tag (gtag.js) - Google Analytics