实现前缀树

  • Post author:
  • Post category:其他


前言

这道题做完,成就感还是满满当当的,嘿嘿!(虽然时间复杂度不是很好,但是空间复杂度很好看)

题目描述

Trie(发音类似 “try”)或者说 前缀树 是一种树形数据结构,用于高效地存储和检索字符串数据集中的键。这一数据结构有相当多的应用情景,例如自动补完和拼写检查。

请你实现 Trie 类:

Trie() 初始化前缀树对象。

void insert(String word) 向前缀树中插入字符串 word 。

boolean search(String word) 如果字符串 word 在前缀树中,返回 true(即,在检索之前已经插入);否则,返回 false 。

boolean startsWith(String prefix) 如果之前已经插入的字符串 word 的前缀之一为 prefix ,返回 true ;否则,返回 false 。

示例:

输入

[“Trie”, “insert”, “search”, “search”, “startsWith”, “insert”, “search”]

[[], [“apple”], [“apple”], [“app”], [“app”], [“app”], [“app”]]

输出

[null, null, true, false, true, null, true]

解释

Trie trie = new Trie();

trie.insert(“apple”);

trie.search(“apple”);   // 返回 True

trie.search(“app”);     // 返回 False

trie.startsWith(“app”); // 返回 True

trie.insert(“app”);

trie.search(“app”);     // 返回 True

提示:

1 <= word.length, prefix.length <= 2000

word 和 prefix 仅由小写英文字母组成

insert、search 和 startsWith 调用次数 总计 不超过 3 * 104 次

思路

主要是创建一个不固定大小的树,节点的结构为:


    struct TreeNode {
        char val;
        vector<struct TreeNode*> children;
        bool flag = false;
    };

之所以设置flag,是为了防止插入重复的字符串部分(比如先插入apple,再插入app)被错误判断.

1)insert函数:我们传入三个参数(string& word, int index, TreeNode* Node),当我们当前的word[index]存在于树中时,我们就递归进入下一层,如果我们是先插入”apple”,再插入”app”,那么我们在最后一个’p’节点的位置,要设置flag=true,这是精髓所在.

void DC(string& word, int index, TreeNode* Node) {      //创造节点
        if (index >= word.size()&&Node==NULL)  return;
        else if (index == word.size()) {
            Node->flag = true;
            return;
        }
        for (int i = 0; i < Node->children.size(); ++i) {
            if (Node->children[i]->val == word[index]) {
                DC(word, index + 1, Node->children[i]);
                return;
            }
        }
        TreeNode* temp = new TreeNode();
        temp->val = word[index];
        Node->children.push_back(temp);
        DC(word, index + 1, temp);
    }

2)search函数:搜索函数返回结果的情况有两种:第一种是查找到底部(eg:insert(“apple”);   search(“apple”)),第二种是查找到index==word.size()时,判断Node->flag是不是为true,为true表示从根节点到当前节点的他们的字符之和为app,那么也可以直接返回true

bool Sr(string& word, int index,TreeNode* Node) {      
        if (index == word.size()) {
            if(Node->children.size() == 0) return true;
            else {
                if (Node->flag)    return true;
            }
        }
        if (index == word.size())  return false;
        for (int i = 0; i < Node->children.size(); ++i) {
            if (Node->children[i]->val == word[index]) {
                return Sr(word, index + 1, Node->children[i]);
            }
        }
        return false;
    }

3)searchWith函数:当index>=word.size()时表示一定存在一条满足条件的路径(这比search函数好理解地多),注意参数中的vec数组表示上一个节点的孩子节点.

  bool SW(string& word, int index, vector<TreeNode*> vec) {
        if (index >= word.size())  return true;
        for (int i = 0; i < vec.size(); ++i) {
            if (vec[i]->val == word[index]) {
                return SW(word, index + 1, vec[i]->children);
            }
        }
        return false;
    }

代码

#include<vector>
#include<string>
using namespace std;

class Trie {
public:

    struct TreeNode {
        char val;
        vector<struct TreeNode*> children;
        bool flag = false;
    };


    TreeNode* head;
    Trie() {
        head = new TreeNode();
        head->val = '#';                //设置#表示头结点,'@'表示NULL
        head->flag = true;              //设置flag表示以当前节点作为结尾的字符串是否是存在的
    }

    void DC(string& word, int index, TreeNode* Node) {      //创造节点
        if (index >= word.size()&&Node==NULL)  return;
        else if (index == word.size()) {
            Node->flag = true;
            return;
        }
        for (int i = 0; i < Node->children.size(); ++i) {
            if (Node->children[i]->val == word[index]) {
                DC(word, index + 1, Node->children[i]);
                return;
            }
        }
        TreeNode* temp = new TreeNode();
        temp->val = word[index];
        Node->children.push_back(temp);
        DC(word, index + 1, temp);
    }

    void insert(string word) {
        DC(word, 0, head);                            //dfs+create=DC
    }

    bool SW(string& word, int index, vector<TreeNode*> vec) {
        if (index >= word.size())  return true;
        for (int i = 0; i < vec.size(); ++i) {
            if (vec[i]->val == word[index]) {
                return SW(word, index + 1, vec[i]->children);
            }
        }
        return false;
    }

    bool Sr(string& word, int index,TreeNode* Node) {      
        if (index == word.size()) {
            if(Node->children.size() == 0) return true;
            else {
                if (Node->flag)    return true;
            }
        }
        if (index == word.size())  return false;
        for (int i = 0; i < Node->children.size(); ++i) {
            if (Node->children[i]->val == word[index]) {
                return Sr(word, index + 1, Node->children[i]);
            }
        }
        return false;
    }

    bool search(string word) {
        return Sr(word, 0, head);
    }

    bool startsWith(string prefix) {
        return SW(prefix, 0, head->children);                     //dfs+find=DF
    }
};

//int main() {
//    Trie* obj = new Trie();
//    obj->insert("apple");
//    obj->insert("app");
//    bool param_2 = obj->search("app");
//    bool param_3 = obj->startsWith("app");
//    return 0;
//}



版权声明:本文为Alanadle原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。