字典树详解

目录

1.什么是字典树?

2.字典树有什么用?

    1.以最节约空间的方式存储大量字符串.且存好后是有序的

    2.快速查询某字符串s在字典树中是否已存在,甚至出现过几次

3.字典树实现思路

4.模版代码

(1).以数组模拟动态分配的只带增查的字典树模版

(2).以动态分配为实现的带增删改查的字典树模版.


 

1.什么是字典树?

    首先字典树是一种数据结构,用于处理大量字符串.,优点在于利用字符串的公共前缀,在存储时节约存储空间,并在查询时最大限度的减少无谓的字符串比较.

2.字典树有什么用?

    1.以最节约空间的方式存储大量字符串.且存好后是有序的

           因为是有序的,故而字典树不仅可用于大量字符串的存储,还可用于大量字符串的排序.

    2.快速查询某字符串s在字典树中是否已存在,甚至出现过几次

          因为当字典树预处理好之后,查询字符串s在当前的出现情况的效率为strlen(s),异常高效,故而常用于搜索引擎等.

3.字典树实现思路

    首先我们已经知道了字典树是一种数据结构,而一个数据结构的重点就在于:

        1.怎么有规则的把数据存储下来

        2.怎么有规则的去高效的得到自己需要的数据

      我们最终的目标就是用程序实现一个如下图的树:

 

字典树详解_第1张图片

        颜色为黑色的表示只是字符串中间的字符,为蓝色的表示是字符串末尾的那个字符,空白那个是树根节点.每个节点的意义实际上就是根节点到这个结点所经过的每个字符组成的字符串.故而图中这个树实际上意味着已存在字符串"","to","tea","ted","ten","a","i","in","inn"

4.模版代码

(1).以数组模拟动态分配的只带增查的字典树模版

//一个只带添加字符串与查找字符串的字典树(为了效率以数组实现) 
 
#include 
#include 
#include 
 
int charmapping[256]; //字符映射数组,charmapping[i]=x表示ascii码为i的字符对应于treenode中的next[x] 
void init_charmapping(){
	for(int i='a';i<='z';i++){ //我的这个字典树现在只允许输入小写字符组成的字符串,然而由于有charmapping的存在,增加新字符添加映射并且增大maxn就好,很方便. 
		charmapping[i]=i-'a';
	} 
} 
 
const int maxn=26; //这里假设字符串中只出现26个小写字母 
const int maxm=100000;
struct treenode{
	bool end; //标志此节点是否是某字符串的结尾 
	treenode* next[maxn]; 
}head;
 
treenode memory[maxm]; //字典树所用到的数组空间
int mallocp=0;  //模拟内存分配
 
void init(){
	head.end=1;
	for(int i=0;iend=0;
	for(int i=0;inext[i]=NULL;
	return newnode;
}
 
void update(char* s){
	int k=0,temp;
	treenode* t=&head;
	while(s[k]){
		temp=charmapping[s[k]];
		if(!t->next[temp]) t->next[temp]=createnew(); 
		t=t->next[temp];
		k++;
	}
	t->end=1;
}
 
bool search(char* s){
	int k=0,temp;
	treenode* t=&head;
	while(s[k]){
		temp=charmapping[s[k]];
		if(!t->next[temp]) return false;
		t=t->next[temp];
		k++;
	}
	if(t->end) return true;
	return false; 
}
 
int main(){
	init_charmapping();
	init();
	char x[1000];
	char t;
	while(1){
		fflush(stdin);
		scanf("%c",&t);
		if(t=='q'){
			scanf("%s",&x);
			if(search(x)) printf("匹配成功!\n");
			else printf("匹配失败!\n"); 
		}
		else if(t=='u'){
			scanf("%s",&x);
			update(x);
			printf("更新完毕!\n");
		}
		else if(t=='e'){
			printf("退出ing....\n"); 
			break;
		}
		else printf("无效命令!,请重新输入!\n"); 
	} 
	return 0;
}

(2).以动态分配为实现的带增删改查的字典树模版.

//一个以链表实现带删除功能允许重复字符串的字典树
#include 
#include 
#include 


int charmapping[256]; //字符映射数组,charmapping[i]=x表示ascii码为i的字符对应于treenode中的next[x] 
void init_charmapping(){
	for(int i='a';i<='z';i++){ //我的这个字典树现在只允许输入小写字符组成的字符串,然而由于有charmapping的存在,增加新字符添加映射并且增大maxn就好,很方便. 
		charmapping[i]=i-'a';
	} 
} 

const int maxn=26; //这里假设字符串中只出现26个小写字母 
const int maxm=100000;
struct treenode{
	int count; //标志此节点所表示字符串在所有字符串中以前缀形式出现的总次数 
	treenode* next[maxn]; 
}head;

void init_trie(){
	head.count=1; //初始化为1包括空串并且避免树头被删 
	for(int i=0;icount=0;
	for(int i=0;inext[i]=NULL;
	return newnode;
}

void update(char* s,int num){ //向字典树添加num个字符串s 
	int k=0,temp;
	treenode* t=&head;
	while(s[k]){
		t->count+=num;
		temp=charmapping[s[k]];
		if(!t->next[temp]) t->next[temp]=createnew(); 
		t=t->next[temp];
		k++;
	}
	t->count+=num;
}

bool search(char* s,int num){  //查找字典树中是否已经存在num个字符串s
	int k=0,temp;
	treenode* t=&head;
	while(s[k]){
		temp=charmapping[s[k]];
		if(!t->next[temp]||t->next[temp]->countnext[temp];
		k++;
	}
	int snum=t->count;
	for(int i=0;inext[i]) snum-=t->next[i]->count; //这里是核心!!!结点t代表的字符串出现的次数就是总次数减去所有子节点次数和 
	if(snum>=num) return true; //如果字符串s的数目snum大于等于num 
	return false;
}

void erase(char* s,int num){  //删除字典树中的num个字符串s并释放无用结点,删除前一定要先search是否存在 
	int k=0,temp;
	treenode* t=&head;
	treenode* t1; //t1后面的结点都是删除后需要被释放的 
	head.count-=num;
	while(s[k]){
		temp=charmapping[s[k]];
		t->next[temp]->count-=num;
		if(t->next[temp]->count==0){
			t1=t->next[temp];
			t->next[temp]=NULL;
			k++;
			break;
		}
		t=t->next[temp];
		k++;
	}
	while(s[k]){ //释放无用结点 
		temp=charmapping[s[k]];
		t=t1->next[temp];
		free(t1);
		t1=t;
		k++;
	}
	free(t1);
}

char temp[1000];
void printall(treenode* tnode,int pos){ //递归打印字典树咯,打出了就是字典序升序的 
	int count=tnode->count;
	for(int i=0;inext[i]) count-=tnode->next[i]->count;
	for(int i=0;inext[charmapping[i]]){
			temp[pos]=i;
			temp[++pos]='\0';
			printall(tnode->next[charmapping[i]],pos);
			temp[--pos]='\0';
		}
	}
}

int main(){
	init_charmapping(); //初始化映射 
	init_trie();		//初始化字典树 
	char x[1000];
	char order; //命令 
	int num;    //数目 
	printf("q:查询\nu:插入\nd:删除\np:打印字典树\ne:退出\n");
	while(1){
		printf("请输入命令:");
		fflush(stdin);
		scanf("%c",&order);
		if(order=='q'){
			printf("请输入要查找的字符串与数目:");
			scanf("%s%d",&x,&num);
			if(search(x,num)) printf("匹配成功。\n\n");
			else printf("匹配失败,不存在%d个\"%s\"\n\n",num,x); 
		}
		else if(order=='u'){
			printf("请输入要插入的字符串与数目:");
			scanf("%s%d",&x,&num);
			update(x,num);
			printf("%d个\"%s\"已加入字典树。\n\n",num,x);
		}
		else if(order=='d'){
			printf("请输入要删除的字符串与数目:");
			scanf("%s%d",&x,&num);
			if(!search(x,num)){
				printf("树中无%d个字符串\"%s\"请重新键入命令!\n\n",num,x);
				continue;
			}
			erase(x,num);
			printf("%d个\"%s\"已从字典树中删除。\n\n",num,x);
		}
		else if(order=='p'){
			printf("当前字典树内有如下字符串:\n");
			temp[0]='\0';
			printall(&head,0);
		}
		else if(order=='e'){
			printf("退出ing....\n"); 
			break;
		}
		else printf("无效命令,请重新输入!\n命令q:查询是否存在字符串\n命令u:往字典树加入字符串\n命令d:删除某个字符串\n命令p:按字典序升序输出字典树\n命令e:退出程序\n\n"); 
	} 
	return 0;
}

 

你可能感兴趣的:(数据结构,算法与数据结构)