1. 程式人生 > >字典樹動態模板增刪查改一

字典樹動態模板增刪查改一

//一個以連結串列實現帶刪除功能允許重複字串的字典樹
#include <stdio.h>
#include <string.h>
#include <stdlib.h>


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;i<maxn;i++) head.next[i]=NULL;
}

treenode* createnew(){ //申請一個新結點並初始化它
	treenode* newnode;
	newnode=(treenode*)malloc(sizeof(treenode));
	newnode->count=0;
	for(int i=0;i<maxn;i++) newnode->next[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]->count<num) return false; //根本不存在字串s或者存在的數目小於num直接失敗
		t=t->next[temp];
		k++;
	}
	int snum=t->count;
	for(int i=0;i<maxn;i++) if(t->next[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;i<maxn;i++) if(tnode->next[i]) count-=tnode->next[i]->count;
	for(int i=0;i<count;i++) printf("\"%s\"\n",temp);
	for(int i='a';i<='z';i++){
		if(tnode->next[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;
}