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树,即字典树,又称单词查找树或键树,是一种树形结构.典型应用是用于统计和排序大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计.它的优点是最大限 ...
随机推荐
- Hbase shell 常用命令(1)
下面我们看看HBase Shell的一些基本操作命令,我列出了几个常用的HBase Shell命令,如下: 名称 命令表达式 创建表 create '表名称', '列名称1','列名称2','列名称N ...
- Windows和linux虚拟机之间联网实现SSH远程连接以及VMware的3种网络模式[NAT、桥接和Host-only]
Windows和linux虚拟机之间联网实现SSH远程连接以及VMware的3种网络模式[NAT.桥接和Host-only] 作者:天齐 一.Windows和linux虚拟机之间联网实现SSH远程连接 ...
- 关于github上开源nineoldandroids兼容动画的笔记
github上面有个样例非常好的列举了NineOldAndroids兼容动画的使用方法.下载了下来然后执行測试了下: 由于要引用nineoldandroids的项目,我这里直接把它的源码拷贝进去了,用 ...
- CentOS 6.5 x64下Hadoop安装
Apache Hadoop安装部署模式 单机(本地)模式(Standalone Mode) 伪分布模式(Pseudo-Distributed Mode) 完全分布模式(Fully Distribute ...
- C语言 · 第二大整数
算法提高 第二大整数 时间限制:1.0s 内存限制:512.0MB 问题描述 编写一个程序,读入一组整数(不超过20个),当用户输入0时,表示输入结束.然后程序将从这组整数中,把第二 ...
- AX88772B 驱动移植
Linux kernel 3.2.0 捏自带的AX88772B 不稳定,现用 AX88772B 官方的驱动进行移植测试. 驱动下载地址: http://www.asix.com.tw/cs/produ ...
- Entity Framework应用:管理并发
理解并发 并发管理解决的是允许多个实体同时更新,实际上这意味着允许多个用户同时在相同的数据上执行多个数据库操作.并发是在一个数据库上管理多个操作的一种方式,同时遵守了数据库操作的ACID属性(原子性. ...
- PHP变量解析顺序variables_order
转载自:http://blog.csdn.net/knight0450/article/details/4291556 故事从一个有点诡异的BUG开始,后台一个使用频率不是很高的广告提交功能有时候会莫 ...
- MySQL的最大连接数
mysql的最大连接数默认是100, 这个数值对于并发连接很多的数据库应用是远远不够的,当连接请求大于默认连接数后,就会出现无法连接数据库的错误,因此我们需要把它适当调大一些 设置新的MySQL最大连 ...
- Tomcat负载均衡和集群环境的搭建
实现此集群的方法参考了网上的很多文章,但由于很多文章都表明是原创的,故无法知道整个操作流程的真正作者是谁.下面就是我用我们真实的项目去实现这个过程.同时修复这过程中一些问题.以下的所有步骤均为亲自测试 ...