《树》专题

1. 二叉树

1.1 二叉树的遍历

1.1.1 前序遍历

【递归版】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;!
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
private:
vector<int> re;
public:
vector<int> preorderTraversal(TreeNode* root) {
if(root!=NULL)
{
re.push_back(root->val);
preorderTraversal(root->left);
preorderTraversal(root->right);
}
return re;
}
};

【非递归版】

利用栈来模拟递归,具体思路需要手动模拟一遍。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
private:
vector<int> re;
stack<TreeNode*> sta;
public:
vector<int> preorderTraversal(TreeNode* root) {
if(root==NULL) return re;
TreeNode* cur = root;
sta.push(cur);

while(!sta.empty())
{
//先获取栈顶元素,然后将其弹出
cur = sta.top();
sta.pop();

re.push_back(cur->val); //按照前序将节点值存到结果数中

//一定要先右后左,原因手动模拟一遍即可
if(cur->right) sta.push(cur->right);
if(cur->left) sta.push(cur->left);
}
return re;
}
};

1.1.2 中序遍历

【递归版】

1
2
3
4
5
6
7
void inorderTraversal(TreeNode* root) {
if(root){
inorderTraversal(root->left);
re.push_back(root->val);
inorderTraversal(root->right);
}
}

【非递归版】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void InorderTraversal(TreeNode* root) 
{
if(root==NULL) return;

TreeNode* temp = root;
stack<TreeNode*> sta;

while(!sta.empty()||temp!=NULL)
{
sta.push(temp);
temp = temp->left;
while(temp==NULL&&!sta.empty())
{
temp = sta.top();
/*Operation*/
sta.pop();
temp = temp->right;
}
}
return re;
}

1.1.3 后序遍历(TODO)

【递归版】

1
2
3
4
5
6
7
void postorderTraversal(TreeNode* root) {
if(root){
postorderTraversal(root->left);
postorderTraversal(root->right);
re.push_back(root->val);
}
}

【非递归版】

1.1.4 层次遍历

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void levelOrder(TreeNode* root) 
{
        if(root==NULLreturn;

        queue<TreeNode*> qu;
        qu.push(root);

        while(!qu.empty())
        {
            int length = qu.size();
            for(int i = 0;i<length;i++)
            {
                TreeNode* temp = qu.front();
                /*operation*/
                if(temp->left!=NULL) qu.push(temp->left);
                if(temp->right!=NULL) qu.push(temp->right);
                qu.pop();
            }
        }
        return re;
    }


1.2 相关属性计算

1.2.1 二叉树的深度

【ac代码】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
int maxDepth(TreeNode* root) {
if(root==NULL) return 0;
else{
int l = maxDepth(root->left);
int r = maxDepth(root->right);
return 1+max(l, r);
}
}
};

1.2.2 二叉树的叶子节点数(TODO)

【递归解法】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
int count = 0;
int countLeaves(TreeNode* root) {
if(root!=NULL){
if(root->left==NULL && root->right==NULL) count++;
countLeaves(root->left);
countLeaves(root->right);
}
return count;
}
};

【迭代解法】

解析:TODO

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
int count = 0;
int countLeaves(TreeNode* root) {
stack<TreeNode*> sta;
while(root!=NULL || !sta.empty()){
sta.push(root);
if(root->left==NULL && root->right==NULL) count++;
root = root->left;
while(root==NULL && !sta.empty()){
root = sta.top();
sta.pop();

root = root->right;
}
}
return count;
}
};

1.2.3 二叉树的节点总数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
int nodeSum(TreeNode* root) {
if(root==NULL) return 0;
return 1+nodeSum(root->left)+nodeSum(root->right);
}
};

1.2.4 某节点所在层次

1
2
3
4
5
6
7
8
9
10
11
12
13
int level(TreeNode* root, TreeNode* tar, int h)//h is 1 initially
{
int l;
if(root==NULLreturn 0;
else if(root==tar) return h;
else
{
l = level(root->left,tar,h+1);
if(l) return l;
else return level(root->right,tar,h+1);
}
}

1.3 哈夫曼树(TODO)

哈夫曼树可以用来实现哈夫曼编码,什么是哈夫曼编码呢?

数据在物理层传输是以比特流(01010….)的形式进行的,传输信息时,按照字符出现频率的大小为其进行编码(即用比如101代表字幕A)可以节省资源,这种按照出现频率编码的的方式就是哈夫曼编码。

1.4 例题

1.4.1 将完全二叉树按层次连接

【题目】

给定一个完全二叉树(所有非叶节点都有两个孩子,所有叶子都在同一层),其中每一个节点都存在next指针,指向其【层次序】的下一个节点。如果当前节点为其所在层的最右侧节点,则其next指针指向NULL。

《层次遍历法》

【解析】

这是最容易想到的解法,在层次遍历的过程中设置next指针。

【ac代码】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/*
// Definition for a Node.
class Node {
public:
int val;
Node* left;
Node* right;
Node* next;

Node() : val(0), left(NULL), right(NULL), next(NULL) {}

Node(int _val) : val(_val), left(NULL), right(NULL), next(NULL) {}

Node(int _val, Node* _left, Node* _right, Node* _next)
: val(_val), left(_left), right(_right), next(_next) {}
};
*/

class Solution {
private:
queue<Node*> que;
public:
Node* connect(Node* root) {
if(root==NULL) return root;
que.push(root);
while(!que.empty()) //层次遍历
{
int length = que.size(); //当前层的总节点数。必须要有这一句,因为下面循环会改变que的大小
for(int i = 0;i<length;i++) //遍历当前层的所有节点
{
Node* head = que.front(); //取出队头
que.pop();

/*如果当前节点不是其所在层的最右侧节点,就将它的next指针指向其【层次序】的下一个节点;
如果当前节点是最右侧节点,其next指针指向NULL(可忽略这一步,因为next指针均已经初始化为NULL了)*/
if(i<length-1) head->next = que.front();

/*扩展,将下一层节点入队*/
if(head->left) que.push(head->left);
if(head->right) que.push(head->right);
}
}
return root;
}
};

《找规律法》

【解析】

当我们完成了一层的next指针连接后,就可以用这一层的节点来对下一层的节点进行next指针连接。

由于题目条件是二叉树,因此规律性很强,更新情况一共就两种,在用第N层更新第N+1层时,一定是这两种更新情况交替出现的。

第一种情况

第二种情况

即用上层的一个节点可以完成下一层的两个next指针连接,做到这一步后,再加上一些边界判断即可。

要注意的是每次进入下一层时,要先保存下下层最左侧的节点,这样才能在当下一层“链表”走完后,能够进入下下层。

【ac代码】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
/*
// Definition for a Node.
class Node {
public:
int val;
Node* left;
Node* right;
Node* next;

Node() : val(0), left(NULL), right(NULL), next(NULL) {}

Node(int _val) : val(_val), left(NULL), right(NULL), next(NULL) {}

Node(int _val, Node* _left, Node* _right, Node* _next)
: val(_val), left(_left), right(_right), next(_next) {}
};
*/

class Solution {
public:
Node* connect(Node* root) {
if(root==NULL) return NULL;
Node* cur = root; //进入第0层,因为只有一个节点,所以可以看作该层的next指针已经连接好了
Node* nextFirst = cur->left; //进入一个新的层级(现在是第0层)先要保存该层下一层最左侧的节点(即下一层的头节点),这样才能在当前层级遍历完后找到下一层的位置

while(nextFirst) //如果还有下一层,继续循环
{
while(cur) //如果当前层还没走到头
{
cur->left->next = cur->right; //进行第一种情况的连接
if(cur->next==NULL) break; //若已经走到当前层的尾部,就跳出当前层试图进入下一层
cur->right->next = cur->next->left; //进行第二种情况的连接
cur = cur->next; //cur指针在当前链表中前进
}
/*跳出循环证明当前层已经走到头了,试图进入下一层*/

cur = nextFirst; //将cur指针移动到已经记录好的下一层开头处
nextFirst = cur->left; //进入一个新的层级后,保存下一层的头结点
}
return root;
}
};

1.4.2 相同的树

【题目】

https://leetcode-cn.com/problems/same-tree/

即判断两颗二叉树是否相同

【解析】

递归地同时遍历两颗二叉树,判断走到的对应位置节点是否相同,只要不相同就返回false,如果都相同就返回true

【ac代码】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
bool isSameTree(TreeNode* t1, TreeNode* t2) {
if(!t1 && !t2) return true; //如果遍历完两颗二叉树了(操作指针同时走到null),且遍历过程中所有对应位置上的节点值相同,则返回true
else if(!t1 || !t2) return false; //如果当前两个操作指针走到位置不同时为null,说明两个二叉树结构不同
else if(t1->val!=t2->val) return false; //如果当前两个操作指所在位置都不为null,那么如果它们值不相同,就返回false
else return isSameTree(t1->left,t2->left) && isSameTree(t1->right, t2->right); //如果当前两个操作指针所在位置都不为null,且值相等,就继续往下判断
}
};

1.4.3 二叉树的层平均值

【题目】

https://leetcode-cn.com/problems/average-of-levels-in-binary-tree/

求二叉树每一层的平均值

【解析】

看1.1.4内容

【ac代码】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<double> averageOfLevels(TreeNode* root) {
vector<double> re;
queue<TreeNode*> que;
que.push(root);

while(!que.empty()){
double sum = 0;

int length = que.size();
for(int i = 0;i<length;i++){
TreeNode* head = que.front();
que.pop();

sum += head->val;

if(head->left) que.push(head->left);
if(head->right) que.push(head->right);
}
re.push_back(sum/length);
}

return re;
}
};

2. Trie树

用途:高效的存储和查找字符串集合。比较典型的应用就是搜索引擎的关键字联想,输入法输入预测,路由器最长前缀匹配,拼写检查。

用Trie树存储的字符串集合一般为同一类型,纯大写字母、纯小写字母或者纯数字。

比如存储hello, her, hell

开始存储her

存储hell

以上为存储过程,查询用类似方法即可完成。
可以发现trie树其实是一个多叉树,如果规定字符串全部由小写字母组成(如上例),则为26叉树。

2.1 实现Trie树

2.1.1 用类实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
class Trie {

private:
Trie* next[26];
bool isEnd;

public:
/** Initialize your data structure here. */
Trie() {
isEnd = false;
memset(next, 0, sizeof(next));
}

/** Inserts a word into the trie. */
void insert(string word) {
Trie* node = this;
char letter;
for(int i = 0; i<word.size();i++)
{
letter = word[i];
if(node->next[letter-'a']==NULL)
{
node->next[letter-'a'] = new Trie();
}
node = node->next[letter-'a'];
}
node->isEnd = true;
}

/** Returns if the word is in the trie. */
bool search(string word) {
Trie* node = this;
char letter;
for(int i = 0;i<word.size();i++)
{
letter = word[i];
if(node->next[letter-'a']==NULL) return false;
node = node->next[letter-'a'];
}
if(node->isEnd) return true;
else return false;
}

/** Returns if there is any word in the trie that starts with the given prefix. */
bool startsWith(string prefix) {
Trie* node = this;
char letter;
for(int i = 0;i<prefix.size();i++)
{
letter = prefix[i];
if(node->next[letter-'a']==NULL) return false;
node = node->next[letter-'a'];
}
return true;
}
};

2.1.2 用数组模拟实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include<iostream>
using namespace std;
int const N = 100010; //字符串最大长度
int trie[N][26]; //用来模拟trie树的数组
int idx; //最近使用过的结点(当前数组下标)
int cnt[N]; //如果存在子字符串,它记录了停止位置;若有多个相同字符串,它记录了总数量。

void insert(string a)
{
int p = 0; //操作指针
for(int i = 0;i<a.size();i++)
{
int u = a[i]-'a'; //将小写字母映射为0~25
if(!trie[p][u]) trie[p][u] = ++idx; //如果当前结点p没有u这个子节点,则创建它(把它放到idx的下一个位置)
p = trie[p][u]; //把操作指针移动到p结点的子节点u处
}
/*可以看到,上面的p代表了所有结点,而无法反映当前所在树的层数。因此每一步我们能获取的信息
只有:数组中p下标对应的结点的有没有儿子u,而没有用到层数的概念.
比如trie[0][3]=3代表了根节点的值为d(即3+'a')的子节点存储在数组下标为3的位置,之后又有trie[3][2]=4就代表数组下标为3位置的结点(也就是d)的值为c(即2+'a')的子节点,该子结点保存在数组下标4的位置。*/
cnt[p]++; //记录string a的出现次数
}

int query(string a)
{
int p = 0;
for(int i = 0;i<a.size();i++)
{
int u = a[i]-'a';
if(!trie[p][u]) return 0; //如果某个结点不匹配,就代表string a不在trie树中
p = trie[p][u]; //如果匹配,就把操作指针移动到当前结点的子结点位置,继续比对
}
return cnt[p]; //返回string a出现的次数
}

/*
可以想象整个结构就是一个巨大的数组,其中的每个元素对应一个结点,因此每个元素都有26个分支。每一个被插入了的字符,都会被保存在两个地方,一个是作为子节点保存在它父结点的26个分支之一中,另一个是作为父结点保存在数组中。
*/

2.2 单词替换

【题目】

给出一个string类型的词根数组dictionary和一个代表句子sentence的字符串(每个单词之间用空格连接,且只包含小写字母),请将sentence中所有其词根出现在dictionary中的单词替换为词根。

输入:dictionary = [“cat”,”bat”,”rat”], sentence = “the cattle was rattled by the battery”
输出:”the cat was rat by the bat”

【解析】

将dictionary中所有词根存入trie树,给出一个方法Prefix(string str)判断传入的字符串的词根是否存在trie树中,如果存在,则返回该词根,否则返回空串。

接着遍历sentence所有单词,对每一个单词调用Prefix方法,如果为空串就保持原有单词,若为词根就用词根替换原有单词。

【ac代码】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
/*自定义Trie树类*/
class Trie{
public:
Trie(){
memset(this->next, 0, sizeof(next));
isEnd = false;
}
void insert(string str){
Trie* node = this;
int length = str.size();
for(int i = 0;i<length;i++){
if(node->next[str[i]-'a']==NULL) node->next[str[i]-'a'] = new Trie();
node = node->next[str[i]-'a'];
}
node->isEnd = true;
}

/*
If the prefix of str is recorded in Trie, return it's prefix
Else return empty string
*/
string Prefix(string str){
Trie* node = this;
int length = str.size();
string re = "";
for(int i = 0;i<length;i++){
if(node->next[str[i]-'a']==NULL) return "";
re += str[i]; //Record the prefix we are finding
node = node->next[str[i]-'a'];
if(node->isEnd) return re; //If meet a prefix before hit the end of str, we found the prefix of str in Trie.
}
return "";
}

private:
Trie* next[27];
bool isEnd;
};


class Solution {
public:
string replaceWords(vector<string>& dictionary, string sentence) {
Trie* head = new Trie();
string re = "";

for(int i = 0;i<dictionary.size();i++) head->insert(dictionary[i]); //将所有词根插入Trie树

int length = sentence.size();
for(int i = 0;i<length;i++){

//遍历sentence中的单词tmp
string tmp = "";
while(i<length && sentence[i]!=' ') tmp+=sentence[i++];

if(head->Prefix(tmp)=="") re+=tmp; //如果单词tmp的词根不存在dictionary中,保持原有单词
else re+=(head->Prefix(tmp));//若存在dictionary中,则将原有单词替换为其词根
if(i<length-1) re+=" ";
}
return re;
}
};

2.3 统计只差一个字符的子串数目

【题目】

https://leetcode-cn.com/problems/count-substrings-that-differ-by-one-character/

【解析】

将t串的所有子串全部访入Trie,然后遍历s串的所有子串,对每一个子串每一个位置上的元素尝试替换为其他小写字母,然后判断替换后的子串是否存在Trie树中。

【ac代码】

该方法超时了,然而其他方法能过的方法没用到Trie树。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
/*Implement Trie*/
class Trie{
public:
Trie(){
memset(next, 0, sizeof(next));
cnt = 0;
}

void insert(string str){
Trie* node = this;
int length = str.size();
for(int i = 0;i<length;i++){
int value = str[i]-'a';
if(node->next[value]==NULL) node->next[value] = new Trie();
node = node->next[value];
}
node->cnt++; //count the occurrence of str
}

/*return the occurrence of str*/
int search(string str){
Trie* node = this;
int length = str.size();
for(int i = 0;i<length;i++){
int value = str[i]-'a';
if(node->next[value]==NULL) return 0;
node = node->next[value];
}
if(node->cnt) return node->cnt;
return 0;
}
private:
Trie* next[26];
int cnt;
};
class Solution {
public:
int countSubstrings(string s, string t) {
Trie head;
int length = t.size();

for(int i = 0; i<length; i++){ //把字符串t的所有子串插入Trie
string sub = "";
for(int j = i; j<length; j++){
sub += t[j];
head.insert(sub);
}
}

length = s.size();
int count = 0; //计算答案
for(int i = 0; i<length; i++){
string sub = "";
for(int j = i; j<length; j++){
sub += s[j]; //对于字符串s的每一个子串sub
for(int k = 0; k<sub.size(); k++){ //遍历sub
char save = sub[k]; //之后会replace掉位置k的元素,因此先保存
for(char p = 'a'; p<='z'; p++){ //对sub每一个位置k上的元素,尝试将它替换为其他字母
if(p==save) continue;//跳过它原本的元素
string newStr = sub.replace(k,1,1,p); //替换k上的元素后得到新字符串
int result = head.search(newStr);//获取新字符串在Trie中出现的次数
if(result){
count+=result;
continue;
}
}
sub.replace(k,1,1,save);//将字符串sub复原,开始尝试替换k+1位置的元素
}
}
}
return count;
}
};

2.4 能够添加与搜索单词(”.”匹配任意字母)的数据结构设计

【题目】

请你设计一个数据结构,支持 添加新单词 和 查找字符串是否与任何先前添加的字符串匹配 。

实现词典类 WordDictionary :

WordDictionary() 初始化词典对象
void addWord(word) 将 word 添加到数据结构中,之后可以对它进行匹配
bool search(word) 如果数据结构中存在字符串与 word 匹配,则返回 true ;否则,返回  false 。word 中可能包含一些 '.' ,每个 . 都可以表示任何一个字母。

1 <= word.length <= 500
addWord 中的 word 由小写英文字母组成
search 中的 word 由 ‘.’ 或小写英文字母组成
最调用多 50000 次 addWord 和 search

【解析】

只要修改Trie树实现的search方法即可:如果当前遍历到的字符为”.”,那么就遍历所有26个字符,尝试匹配到Trie树下一层。

【ac代码】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
class WordDictionary {
public:
/** Initialize your data structure here. */
WordDictionary() {
memset(next, 0, sizeof(next));
isEnd = false;
}

/** Adds a word into the data structure. */
void addWord(string word) {
WordDictionary* node = this;
int length = word.size();
for(int i = 0;i<length;i++){
if(node->next[word[i]-'a']==NULL) node->next[word[i]-'a'] = new WordDictionary();
node = node->next[word[i]-'a'];
}
node->isEnd = true;
}

/** Returns if the word is in the data structure. A word could contain the dot character '.' to represent any one letter. */
bool search(string word){
return search(word, *this);
}
bool search(string word, WordDictionary& root) {
WordDictionary* node = &root;
int length = word.size();
for(int i = 0;i<length;i++){
if(word[i]=='.'){ //如果当前字符为'.'
for(int j = 0;j<26;j++){ //遍历所有26个字母,尝试匹配到Trie树的下一层
if(node->next[j]){
if(search(word.substr(i+1), *node->next[j])) return true;
}
}
return false; //如果说遍历了所有26个字母都无法匹配到Trie树的下一层,说明匹配失败
}
if(node->next[word[i]-'a']==NULL) return false;
node = node->next[word[i]-'a'];
}
return node->isEnd;
}

private:
WordDictionary* next[26];
bool isEnd;
};

/**
* Your WordDictionary object will be instantiated and called as such:
* WordDictionary* obj = new WordDictionary();
* obj->addWord(word);
* bool param_2 = obj->search(word);
*/

2.5 键值映射

【题目】

https://leetcode-cn.com/problems/map-sum-pairs/

【解析】

在前缀树的基础上,为每一个节点维护一个<string, int>型的哈希表。执行insert操作时,每创建一个节点,都往该节点的哈希表中写入当前请求insert的【string、value】对(如果该键值对已存在,则更新它对应的value;不存在,则创建)。

调用sum时指针走到prefix末尾,然后计算指针当前所在节点的哈希表中所有键值对的值之和即可。

【ac代码】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
class MapSum {
public:
MapSum* next[26];
unordered_map<string, int> count;
bool isEnd;

/** Initialize your data structure here. */
MapSum() {
for(int i = 0;i<26;i++) next[i] = NULL;
isEnd = false;
}

void insert(string key, int val) {
MapSum* node = this;
int length = key.size();
for(int i = 0;i<length;i++){
int x = key[i]-'a';
if(node->next[x]==NULL) node->next[x] = new MapSum();

//每走到一个节点,都往该节点的哈希表中写入当前请求insert的【string、value】对(如果该键值对已存在,则更新它;不存在,则创建)
node->next[x]->count[key] = val;
node = node->next[x];
}
node->isEnd = true;
}

int sum(string prefix) {
int length = prefix.size();
MapSum* node = this;
for(int i = 0;i<length;i++){ //指针在前缀树中走到prefix末尾
int x = prefix[i]-'a';
if(node->next[x]==NULL) return 0;
node = node->next[x];
}

/*计算当前节点的哈希表中所有键值对的值的总和*/
int re = 0;
for(auto i = node->count.begin(); i!=node->count.end(); i++){
re += i->second;
}
return re;
}
};

/**
* Your MapSum object will be instantiated and called as such:
* MapSum* obj = new MapSum();
* obj->insert(key,val);
* int param_2 = obj->sum(prefix);
*/

3. 二叉树、树、森林之间的转化(TODO)