Trie树(字典树)(1)
Trie树。又称字典树,单词查找树或者前缀树,是一种用于高速检索的多叉树结构。
Trie树与二叉搜索树不同,键不是直接保存在节点中,而是由节点在树中的位置决定。
一个节点的全部子孙都有同样的前缀(prefix),也就是这个节点相应的字符串,而根节点相应空字符串。普通情况下。不是全部的节点都有相应的值,仅仅有叶子节点和部分内部节点所相应的键才有相关的值。
A trie, pronounced “try”, is a tree that exploits some structure in the keys
- e.g. if the keys are strings, a binary search tree would compare the entire strings, but a trie would look at their individual characters
- Suffix trie are a space-efficient data structure to store a string that allows many kinds of queries to be answered quickly.
- Suffix trees are hugely important for searching large sequences.
Trie树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计。排序和保存大量的字符串(但不仅限于字符串)。所以常常被搜索引擎系统用于文本词频统计。
一个典型的应用,就是在搜索时出现的搜索提示,比方我输入“花千”,就会出现“花千骨电视剧”,“花千骨小说”等提示。
Let word be a single string and let dictionary be a large set of words. If we have a dictionary, and we need to know if a single word is inside of the dictionary the tries are a data structure that can help us. But you may be asking yourself, “Why use tries if set and hash tables can do the same?” There are two main reasons:
1)The tries can insert and find strings in O(L) time (where L represent the length of a single word). This is much faster than set , but is it a bit faster than a hash table.
2)The set and the hash tables can only find in a dictionary words that match exactly with the single word that we are finding; the trie allow us to find words that have a single character different, a prefix in common, a character missing, etc.
Trie树的基本性质能够归纳为:
1)根节点不包括字符,除根节点外的每一个节点仅仅包括一个字符。
2)从根节点到某一个节点。路径上经过的字符连接起来,为该节点相应的字符串。
3)每一个节点的全部子节点包括的字符串不同样。
Trie树的基本实现
字典树的插入(Insert)、删除( Delete)和查找(Find)都很easy。用一个一重循环就可以,即第i 次循环找到前i 个字母所相应的子树,然后进行相应的操作。实现这棵字母树,我们用最常见的数组保存(静态开辟内存)就可以。当然也能够开动态的指针类型(动态开辟内存)。至于结点对儿子的指向,一般有三种方法:
1)对每一个结点开一个字母集大小的数组,相应的下标是儿子所表示的字母,内容则是这个儿子相应在大数组上的位置,即标号。
2)对每一个结点挂一个链表。按一定顺序记录每一个儿子是谁。
3)使用左儿子右兄弟表示法记录这棵树。
三种方法,各有特点。
第一种易实现。但实际的空间要求较大;另外一种。较易实现。空间要求相对较小,但比較费时;第三种,空间要求最小,但相对费时且不易写。
这里採用第一种:
#include <stdio.h>
#include <iostream>
using namespace std;
#define MAX 26
typedef struct TrieNode
{
bool isEnd;
int nCount; // 该节点前缀出现的次数
struct TrieNode *next[MAX]; //该节点的兴许节点
} TrieNode;
TrieNode Memory[1000000]; //先分配好内存。 malloc 较为费时
int allocp = 0;
//初始化一个节点。nCount计数为1。 next都为null
TrieNode * createTrieNode()
{
TrieNode * tmp = &Memory[allocp++];
tmp->isEnd = false;
tmp->nCount = 1;
for (int i = 0; i < MAX; i++)
tmp->next[i] = NULL;
return tmp;
}
void insertTrie(TrieNode * root, char * str)
{
TrieNode * tmp = root;
int i = 0, k;
//一个一个的插入字符
while (str[i])
{
k = str[i] - 'a'; //当前字符 应该插入的位置
if (tmp->next[k])
{
tmp->next[k]->nCount++;
}
else
{
tmp->next[k] = createTrieNode();
}
tmp = tmp->next[k];
i++; //移到下一个字符
}
tmp->isEnd = true;
}
int searchTrie(TrieNode * root, char * str)
{
if (root == NULL)
return 0;
TrieNode * tmp = root;
int i = 0, k;
while (str[i])
{
k = str[i] - 'a';
if (tmp->next[k])
{
tmp = tmp->next[k];
}
else
return 0;
i++;
}
return tmp->nCount; //返回最后的那个字符 所在节点的 nCount
}
/* During delete operation we delete the key in bottom up manner using recursion. The following are possible conditions when deleting key from trie:
Key may not be there in trie. Delete operation should not modify trie.
Key present as unique key (no part of key contains another key (prefix), nor the key itself is prefix of another key in trie). Delete all the nodes.
Key is prefix key of another long key in trie. Unmark the leaf node.
Key present in trie, having atleast one other key as prefix key. Delete nodes from end of key until first leaf node of longest prefix key. */
bool deleteTrie(TrieNode * root, char * str)
{
TrieNode * tmp = root;
k = str[0] - 'a';
if(tmp->next[k] == NULL)
return false;
if(str == ‘\0’)
return false;
if(tmp->next[k]->isEnd && str[1] == ‘\0’)
{
tmp->next[k]->isEnd = false;
tmp->next[k]->nCount--;
if(tmp->next[k]->nCount == 0) //really delete
{
tmp->next[k] = NULL;
return true;
}
return false;
}
if(deleteTrie(tmp->next[k], str+1)) //recursive
{
tmp->next[k]->nCount--;
if(tmp->next[k]->nCount == 0) //really delete
{
tmp->next[k] = NULL;
return true;
}
return false;
}
}
int main(void)
{
char s[11];
TrieNode *Root = createTrieNode();
while (gets(s) && s[0] != '0') //读入0 结束
{
insertTrie(&Root, s);
}
while (gets(s)) //查询输入的字符串
{
printf("%d\n", searchTrie(Root, s));
}
return 0;
}
应用例一:
Longest prefix matching – A Trie based solution
Given a dictionary of words and an input string, find the longest prefix of the string which is also a word in dictionary.
Examples:
Let the dictionary contains the following words:
{are, area, base, cat, cater, children, basement}
Below are some input/output examples:
Input String Output
caterer cater
basemexy base
child < Empty >
Solution:
We build a Trie of all dictionary words. Once the Trie is built, traverse through it using characters of input string. If prefix matches a dictionary word, store current length and look for a longer match. Finally, return the longest match.
// The main method that finds out the longest string 'input'
public String getMatchingPrefix(String input) {
String result = ""; // Initialize resultant string
int length = input.length(); // Find length of the input string
// Initialize reference to traverse through Trie
TrieNode crawl = root;
// Iterate through all characters of input string 'str' and traverse
// down the Trie
int level, prevMatch = 0;
for( level = 0 ; level < length; level++ )
{
// Find current character of str
char ch = input.charAt(level);
// HashMap of current Trie node to traverse down
HashMap<Character,TrieNode> child = crawl.getChildren();
// See if there is a Trie edge for the current character
if( child.containsKey(ch) )
{
result += ch; //Update result
crawl = child.get(ch); //Update crawl to move down in Trie
// If this is end of a word, then update prevMatch
if( crawl.isEnd() )
prevMatch = level + 1;
}
else break;
}
// If the last processed character did not match end of a word,
// return the previously matching prefix
if( !crawl.isEnd() )
return result.substring(0, prevMatch);
else return result;
}
应用例二:
Print unique rows in a given boolean matrix
Given a binary matrix, print all unique rows of the given matrix.
Input:
{0, 1, 0, 0, 1}
{1, 0, 1, 1, 0}
{0, 1, 0, 0, 1}
{1, 1, 1, 0, 0}
Output:
0 1 0 0 1
1 0 1 1 0
1 1 1 0 0
Method 1 (Simple)
A simple approach is to check each row with all processed rows. Print the first row. Now, starting from the second row, for each row, compare the row with already processed rows. If the row matches with any of the processed rows, don’t print it. If the current row doesn’t match with any row, print it.
Time complexity: O( ROW^2 x COL )
Auxiliary Space: O( 1 )
Method 2 (Use Binary Search Tree)
Find the decimal equivalent of each row and insert it into BST. Each node of the BST will contain two fields, one field for the decimal value, other for row number. Do not insert a node if it is duplicated. Finally, traverse the BST and print the corresponding rows.
Time complexity: O( ROW x COL + ROW x log( ROW ) )
Auxiliary Space: O( ROW )
This method will lead to Integer Overflow if number of columns is large.
Method 3 (Use Trie data structure)
Since the matrix is boolean, a variant of Trie data structure can be used where each node will be having two children one for 0 and other for 1. Insert each row in the Trie. If the row is already there, don’t print the row. If row is not there in Trie, insert it in Trie and print it.
Below is C implementation of method 3.
//Given a binary matrix of M X N of integers, you need to return only unique rows of binary array
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#define ROW 4
#define COL 5
// A Trie node
typedef struct Node
{
bool isEndOfCol;
struct Node *child[2]; // Only two children needed for 0 and 1
} Node;
// A utility function to allocate memory for a new Trie node
Node* newNode()
{
Node* temp = (Node *)malloc( sizeof( Node ) );
temp->isEndOfCol = 0;
temp->child[0] = temp->child[1] = NULL;
return temp;
}
// Inserts a new matrix row to Trie. If row is already
// present, then returns 0, otherwise insets the row and
// return 1
bool insert( Node** root, int (*M)[COL], int row, int col )
{
// base case
if ( *root == NULL )
*root = newNode();
// Recur if there are more entries in this row
if ( col < COL )
return insert ( &( (*root)->child[ M[row][col] ] ), M, row, col+1 );
else // If all entries of this row are processed
{
// unique row found, return 1
if ( !( (*root)->isEndOfCol ) )
return (*root)->isEndOfCol = 1;
// duplicate row found, return 0
return 0;
}
}
// A utility function to print a row
void printRow( int (*M)[COL], int row )
{
int i;
for( i = 0; i < COL; ++i )
printf( "%d ", M[row][i] );
printf("\n");
}
// The main function that prints all unique rows in a
// given matrix.
void findUniqueRows( int (*M)[COL] )
{
Node* root = NULL; // create an empty Trie
int i;
// Iterate through all rows
for ( i = 0; i < ROW; ++i )
// insert row to TRIE
if ( insert(&root, M, i, 0) )
// unique row found, print it
printRow( M, i );
}
// Driver program to test above functions
int main()
{
int M[ROW][COL] = {{0, 1, 0, 0, 1},
{1, 0, 1, 1, 0},
{0, 1, 0, 0, 1},
{1, 0, 1, 0, 0}
};
findUniqueRows( M );
return 0;
}
Time complexity: O( ROW x COL )
Auxiliary Space: O( ROW x COL )
This method has better time complexity. Also, relative order of rows is maintained while printing.
Trie树(字典树)(1)的更多相关文章
- 剑指Offer——Trie树(字典树)
剑指Offer--Trie树(字典树) Trie树 Trie树,即字典树,又称单词查找树或键树,是一种树形结构,是一种的单词.对于每一个单词,我们要判断他出没出现过,如果出现了,求第一次出现在第几个位 ...
- AC自动机——1 Trie树(字典树)介绍
AC自动机——1 Trie树(字典树)介绍 2013年10月15日 23:56:45 阅读数:2375 之前,我们介绍了Kmp算法,其实,他就是一种单模式匹配.当要检查一篇文章中是否有某些敏感词,这其 ...
- Trie(字典树)
没时间整理了,老吕又讲课了@ @ 概念 Trie即字典树,又称单词查找树或键树,是一种树形结构,是一种哈希树的变种,典型应用是统计和排序大量的字符串(不限于字符串) Trie字典树主要用于存储字符串, ...
- 9-11-Trie树/字典树/前缀树-查找-第9章-《数据结构》课本源码-严蔚敏吴伟民版
课本源码部分 第9章 查找 - Trie树/字典树/前缀树(键树) ——<数据结构>-严蔚敏.吴伟民版 源码使用说明 链接☛☛☛ <数据结构-C语言版>(严蔚 ...
- [LintCode] Implement Trie 实现字典树
Implement a trie with insert, search, and startsWith methods. Have you met this question in a real i ...
- Trie树|字典树(字符串排序)
有时,我们会碰到对字符串的排序,若采用一些经典的排序算法,则时间复杂度一般为O(n*lgn),但若采用Trie树,则时间复杂度仅为O(n). Trie树又名字典树,从字面意思即可理解,这种树的结构像英 ...
- Trie - leetcode [字典树/前缀树]
208. Implement Trie (Prefix Tree) 字母的字典树每个节点要定义一个大小为26的子节点指针数组,然后用一个标志符用来记录到当前位置为止是否为一个词,初始化的时候讲26个子 ...
- Trie树/字典树题目(2017今日头条笔试题:异或)
/* 本程序说明: [编程题] 异或 时间限制:1秒 空间限制:32768K 给定整数m以及n个数字A1,A2,..An,将数列A中所有元素两两异或,共能得到n(n-1)/2个结果,请求出这些结果中大 ...
- Trie树(字典树)的介绍及Java实现
简介 Trie树,又称为前缀树或字典树,是一种有序树,用于保存关联数组,其中的键通常是字符串.与二叉查找树不同,键不是直接保存在节点中,而是由节点在树中的位置决定.一个节点的所有子孙都有相同的前缀,也 ...
- Trie树 - 字典树
1.1.什么是Trie树 Trie树,即字典树,又称单词查找树或键树,是一种树形结构.典型应用是用于统计和排序大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计.它的优点是最大限 ...
随机推荐
- LeetCode: Max Points on a Line 解题报告
Max Points on a Line Given n points on a 2D plane, find the maximum number of points that lie on the ...
- ubuntu16.04中文乱码解决方案
把环境变量设置为中文 </pre><pre name="code" class="plain">sudo vi /etc/default ...
- 【AUC】二分类模型的评价指标ROC Curve
AUC是指:从一堆样本中随机抽一个,抽到正样本的概率比抽到负样本的概率大的可能性! AUC是一个模型评价指标,只能用于二分类模型的评价,对于二分类模型,还有很多其他评价指标,比如logloss,acc ...
- 进制转化之递归 && 栈
将10进制转换成2进制,是除以2得到的余数,再倒序排列,这可以用递归实现,也可以用数据结构——栈实现. 先看递归实现: #include<stdio.h> void to_two(int ...
- 计时器,主要讲了setInterval()和Date()方法
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- jQuery (一)选择器
上一章开始了jQuery的安装,这一张需要开始学习选择器了,不然不进行选择,就无法使用jQuery提供的库的功能不是. 常用的,就列举这么多吧 <!DOCTYPE html> <ht ...
- java 关于同步异步的理解
经常看到介绍 ArrayList 和HashMap是异步,Vector和HashTable是同步,这里同步是线程安全的,异步不是线程安全的,举例说明: 当创建一个Vector对象时候, Vector ...
- .net安装部署“Error 1001 在初始化安装时发生异常” 的解决方法
状况描述:打包安装后,如果删除安装目录中的某个文件,这时从桌面快捷方式启动软件系统会自动运行修复程序,此时因为路径问题会报出“错误 1001 在初始化安装时发生异常xxx”的异常.(前提是你的安装部署 ...
- C语言 · 运用结构体的排序方法
之前遇到排序只想着最原始的方法,诸如冒泡,选择,快速排序等等,刚刚跟大牛学会了结构体的方法来排序,这样的话以后再也不用怕成绩统计.名次排序之类的题目了. 首先头文件(基于大牛的方法,本人之后做题喜欢引 ...
- Windows下Postgresql数据库的下载与配置方法
注意下载的是二进制版,不是带Windows Installer的,即绿色版本 http://www.enterprisedb.com/products-services-training/pgbind ...