目錄
1 字典樹的概念
2字典樹的套路
2 .1 字典樹的特點
2.2實現方法【通用模板】
3. Leetcode 648 單詞替換
1 字典樹的概念
在算法導論中,Trie並不是叫字典樹,而是叫基數樹,也就是說實際上並不是只有和字符串有關。字典樹實際上是一個N叉樹 。 在這個N 二叉樹中,如果是共父節點的N個子節點是有序的,這樣構造出來的樹就和字典很像了,故叫字典樹。
字典樹的功能實際上市對於很多的串進行壓縮,壓縮的方法是根據這個字符串的前綴。具體的說,就是每個字典樹的接管將表示一個字符,用從根節點到葉子節點來表示一個字符串。【好繞啊,看下面的圖 ==】
2字典樹的套路
2 .1 字典樹的特點
Trie Tree特點:
1. 根節點不包含字符, 除根節點外每一個節點都只包含一個字符。
2. 從根節點到某一節點, 路徑上經過的字符連接起來, 爲該節點對應的字符串。
3. 在trie樹中查找一個關鍵字的時間和樹中包含的結點數無關, 而取決於組成關鍵字的字符數。 也就是查找字符串s的時間爲O(strlen(s))
4. 如果要查找的關鍵字可以分解成字符序列且不是很長, 利用Trie樹查找速度優於二叉查找樹。
如:若關鍵字長度最大是5, 則利用Trie樹, 利用5次比較可以從265=11881376個可能的關鍵字中檢索出指定的關鍵字。 而利用二叉查找樹至少要進行log2265=23.5次比較。
2.2實現方法【通用模板】
定義:
/* 定義Trie */
typedef struct _info_trie
{
int flag; /* 標記位,對應單詞的編號 */
struct _info_trie *nxt[26];
}info_trie;
查找:
int Find(char *s) {
int t,
int p = 1;
int len = strlen;
for (int i = 0; i < len; i ++) {
t = s[i] - 'a';
if (trie[p]->nxt[t] == NULL) {
return 0; /* 當前要匹配值爲t的字母,若沒有則結束 */
}
p = trie[p]->nxt[t]; /* 若存在值爲t的字母,則繼續匹配 */
}
return trie[p]->flag; /* 若for循環執行完畢,說明找到了需要的單詞,返回其編號 */
}
插入:
void Insert(int *s, int k) {
int t;
int p = 1;
int len = strlen(s);
for (int i = 0; i < len; i ++) {
/* 將字符c[i]轉換成值爲0到25的數字,比如'a'轉換爲0,'b'轉換爲1,‘c’轉換爲2… */
t = c[i] - 'a';
if (trie[p]->nxt[t] == NULL) { /* 若p沒有值爲t的兒子 */
tot++; / * 新增一個編號爲tot的節點 */
trie[p]->nxt[t] = tot; /* 記下p的值爲t的孩子節點的編號 */
p = trie[p]->nxt[t]; /* p指向新添加的節點 */
trie[p]->flag = 0; /* 初始化新添加的節點,將其標記爲不是單詞的結尾
} else {
p = trie[p]->nxt[t]; /* 若p存在值爲t的兒子,p指向該兒子,繼續討論 */
}
}
trie[p]->flag = k; //for循環已執行完,說明第k個單詞已加入,在單詞結尾做上標記
}
3. Leetcode 648 單詞替換
LeetCode 648 : 單詞替換 的完整答案如下
考試需要,最近一律《C語言版本》。
#define maxSize 100000
/* 定義Trie */
typedef struct _info_st
{
int flag; /* 標記位,對應單詞的編號 */
struct _info_st *nxt[26];
}info_st;
char * replaceWords(char ** dict, int dictSize, char * sentence)
{
/* 構造字典樹 len = maxSize * sizeof(info_st) */
info_st * pool = (info_st *)calloc(maxSize, sizeof(info_st)); /* info_st * pool = (info_st *)malloc(sizeof(info_st) * maxSize);會報錯 */
int psize = 0;
info_st *root = &pool[psize++];
for(int i = 0; i < dictSize; i++)
{
int wordslen = strlen(dict[i]); /* 字典裏每個單詞的長度 */
info_st *cur = root; /* 定義cur指向root */
for(int j = 0; j < wordslen; j++) {
int nid = dict[i][j] - 'a'; /* 都是小寫字母 */
if(cur->nxt[nid] == NULL) /* 如果沒有子節點,咱們就生成一個 */
{
cur->nxt[nid] = &pool[psize++];
}
cur = cur->nxt[nid];
}
cur->flag++; /* flag = 1, 表示該單詞結束 */
}
int slen = strlen(sentence);
char * res = (char *)calloc(slen + 1, sizeof(char)); /* 改成char *res = (char *)malloc(sizeof(char) * (slen + 1))報錯; */
int rsize = 0;
/* 雙指針遍歷並處理句子中的單詞 */
int wordsStart = 0;
int wordsEnd = 0;
while(wordsEnd < slen) {
/* 遍歷句子中的每個詞 */
if(sentence[wordsEnd] != ' ') {
wordsEnd++;
continue;
}
info_st *cur = root; /* 定義cur指向root */
for(int i = wordsStart; i < wordsEnd; i++) {
if(cur != NULL) {
if(cur->flag > 0) { /* flag > 0 找到了能擴展的子串 */
break;
}
int nid = sentence[i] - 'a';
cur = cur->nxt[nid];
}
res[rsize++] = sentence[i];
}
res[rsize++] = sentence[wordsEnd++]; /* 空格賦值 */
wordsStart = wordsEnd; /* 繼續遍歷句子中的下一個詞 */
}
/* 處理最後一個單詞 */
info_st *cur = root;
for(int i = wordsStart; i < wordsEnd; i++) {
if(cur != NULL) {
if(cur->flag > 0) {
break;
}
int nid = sentence[i] - 'a';
cur = cur->nxt[nid];
}
res[rsize++] = sentence[i];
}
res[rsize++] = '\0';
return res;
}
--@shanghai, 20200206