字典树

概述

字典树,又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:利用字符串的公共前缀来节约存储空间,最大限度地减少无谓的字符串比较,查询效率比哈希表高

性质

字典树的三个性质:
  • 根节点不包含字符,根节点外每一个节点都只包含一个字符
  • 从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串
  • 每个节点的所有子节点包含的字符都不相同


数据结构定义

#define MAX 26	// 字符集大小

typedef struct trieNode {
	struct trieNode *next[MAX];
	int count;	// 记录该字符出现次数
} trieNode;


next数组表示每层有多少类的数,如果只是小写字母,26即可


实现方法

搜索字典项目的方法:
  1. 从根节点开始一次搜索
  2. 获取要查找关键词的第一个字母,并根据该字母选择对应的子树并转到该子树继续进行检索
  3. 在相应的子树上,获取要查找关键词的第二个字母,并进一步选择对应的子树进行检索
  4. 迭代过程
  5. 在某个节点处,关键词的所有字母已被取出,则读取附在该结点上的信息,即完成查找

其他操作类似


实现模板


初始化根结点

/**
 * 初始化Trie树根结点
 */
void initTrie(trieNode **root)
{
	int i;

	*root = (trieNode *)malloc(sizeof(trieNode));
	(*root)->count = 0;

	for (i = 0; i < MAX; i ++) {
		(*root)->next[i] = NULL;
	}
}


插入单词到trie树

/**
 * Trie树插入操作
 */
void insert(char *str, trieNode *root)
{
	int i;

	trieNode *p = root;

	while (*str != '\0') {
		if (p->next[*str - 'a'] == NULL) {
			trieNode *tmp = (trieNode *)malloc(sizeof(trieNode));
			for (i = 0; i < MAX; i ++) {
				tmp->next[i] = NULL;
			}
			tmp->count = 1;
			p->next[*str - 'a'] = tmp;
			p = p->next[*str - 'a'];
		} else {
			p = p->next[*str - 'a'];
			p->count ++;
		}

		str ++;
	}
}


统计查找单词数量

/**
 * 统计前缀出现次数
 */
int count(char *search, trieNode *root)
{
	trieNode *p = root;

	while (*search != '\0') {
		if (p->next[*search - 'a'] == NULL) {
			return 0;
		} else {
			p = p->next[*search - 'a'];
			search ++;
		}
	}

	return p->count;
}

清理trie树

/**
 * 清理trie树
 */
void delTrie(trieNode *root)
{
	int i;

	for (i = 0; i < MAX; i ++) {
		if (root->next[i] != NULL) {
			delTrie(root->next[i]);
		}
	}

	free(root);
}


时间复杂度

插入、查找的时间复杂度均为O(n),n为字符串的长度

空间复杂度较高,O(26^n),典型空间换时间


参考题目

链接: 统计难题

ac代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX 26	// 字符集大小

typedef struct trieNode {
	struct trieNode *next[MAX];
	int count;	// 记录该字符出现次数
} trieNode;


/**
 * 初始化Trie树根结点
 */
void initTrie(trieNode **root)
{
	int i;

	*root = (trieNode *)malloc(sizeof(trieNode));
	(*root)->count = 0;

	for (i = 0; i < MAX; i ++) {
		(*root)->next[i] = NULL;
	}
}

/**
 * Trie树插入操作
 */
void insert(char *str, trieNode *root)
{
	int i;

	trieNode *p = root;

	while (*str != '\0') {
		if (p->next[*str - 'a'] == NULL) {
			trieNode *tmp = (trieNode *)malloc(sizeof(trieNode));
			for (i = 0; i < MAX; i ++) {
				tmp->next[i] = NULL;
			}
			tmp->count = 1;
			p->next[*str - 'a'] = tmp;
			p = p->next[*str - 'a'];
		} else {
			p = p->next[*str - 'a'];
			p->count ++;
		}

		str ++;
	}
}

/**
 * 统计前缀出现次数
 */
int count(char *search, trieNode *root)
{
	trieNode *p = root;

	while (*search != '\0') {
		if (p->next[*search - 'a'] == NULL) {
			return 0;
		} else {
			p = p->next[*search - 'a'];
			search ++;
		}
	}

	return p->count;
}

/**
 * 清理trie树
 */
void delTrie(trieNode *root)
{
	int i;

	for (i = 0; i < MAX; i ++) {
		if (root->next[i] != NULL) {
			delTrie(root->next[i]);
		}
	}

	free(root);
}


int main(void)
{
	char str[15];
	trieNode *root;

	// 初始化根结点
	initTrie(&root);

	while (gets(str) && str[0] != '\0') {
		// 插入Trie树
		insert(str, root);
	}

	// 查找前缀出现次数
	while (gets(str) && str[0] != '\0') {
		printf("%d\n", count(str, root));
	}

	delTrie(root);

	return 0;
}


后记

其实博主写这篇博客,也是因为电话面试涂鸦移动的时候被问到了智能提示,其实智能提示实现不难,trie树+top k,中文的话转成拼音存储即可,我当时虽然回答出来了,但是连连看的题目没回答上,蛋疼

你可能感兴趣的:(字典树)