【问题标题】:why this C++ Trie implementation is showing odd behaviour?为什么这个 C++ Trie 实现表现出奇怪的行为?
【发布时间】:2016-06-29 02:47:57
【问题描述】:

我实现了这个类来创建一个 trie 数据结构。函数

unsigned long Insert(string) //inserts the string in trie & return no of words in trie

void PrintAllWords(); // prints all words in trie separated by space in dictionary order

当单词的数量不是很大时,实现可以正常工作并打印从英语词典单词的文本文件中插入的所有单词,但是当提供一个包含大约 350k 单词的文件时,它只会打印出 a b c d upto z。

私有变量

struct TrieTree
{
    std::map<char,struct TrieTree*> map_child;
    std::map<char,unsigned long> map_count; //keeps incrementing count of char in map during insertion.
    bool _isLeaf=false;  // this flag is set true at node where word ends
};

struct TrieTree* _root=NULL;
unsigned long _wordCount=0;
unsigned long _INITIALIZE=1;

下面是完整的驱动程序实现。该程序是可执行的。

#include<iostream>
#include<map>
#include<fstream>
class Trie
{
private:

    struct TrieTree
    {
        std::map<char,struct TrieTree*> map_child;
        std::map<char,unsigned long> map_count;
        bool _isLeaf=false;
    };

    struct TrieTree* _root=NULL;
    unsigned long _wordCount=0;
    unsigned long _INITIALIZE=1;

    struct TrieTree* getNode()
    {
        return new TrieTree;
    };


    void printWords(struct TrieTree* Tptr,std::string pre)
    {
        if(Tptr->_isLeaf==true)
        {
            std::cout<<pre<<" ";
            return;
        }

        std::map<char,struct TrieTree*>::iterator it;
        it=Tptr->map_child.begin();
        while(it!=Tptr->map_child.end())
        {
            pre.push_back(it->first);
            printWords(it->second,pre);
            pre.erase(pre.length()-1);  //erase last prefix character
            it++;
        }

    }


public:

    Trie()
    {
        _root=getNode();
    }
    unsigned long WordCount()
    {
        return _wordCount;
    }
    unsigned long WordCount(std::string pre) //count words with prefix pre
    {
        if(WordCount()!=0)
        {
            struct TrieTree *Tptr=_root;
            std::map<char,unsigned long>::iterator it;
            char lastChar;
            for(int i=0;i<pre.length()-1;i++)
            {
                Tptr=Tptr->map_child[pre[i]];
            }
            lastChar=pre[pre.length()-1];
            it=Tptr->map_count.find(lastChar);
            if(it!=Tptr->map_count.end())
            {
                return Tptr->map_count[lastChar];
            }
            else
            {
                return 0;
            }
        }
        return 0;
    }

    unsigned long Insert(std::string key)   //return word count after insertion
    {
        struct TrieTree *Tptr =_root;
        std::map<char,struct TrieTree*>::iterator it;

        if(!SearchWord(key))
        {
            for(int level=0;level<key.length();level++)
            {
                it=Tptr->map_child.find(key[level]);
                if(it==Tptr->map_child.end())
                {
                    //alphabet does not exist in map
                    Tptr->map_child[key[level]]=getNode();  // new node with value pointing to it
                    Tptr->map_count[key[level]] = _INITIALIZE;
                    Tptr=Tptr->map_child[key[level]];       //assign pointer to newly obtained node
                    if(level==key.length()-1)
                        Tptr->_isLeaf=true;
                }
                else
                {   //alphabet exists at this level
                    Tptr->map_count[key[level]]++;
                    Tptr=Tptr->map_child[key[level]];
                }
            }
            _wordCount++;
        }
        return _wordCount;
    }

    bool SearchWord(std::string key)
    {
        struct TrieTree *Tptr =_root;
        std::map<char,struct TrieTree*>::iterator it;
        for(int level=0;level<key.length();level++)
        {
            it=Tptr->map_child.find(key[level]);
         //   cout<<" "<<Tptr->map_child.size()<<endl;  //test to count entries at each map level

            if(it!=Tptr->map_child.end())
            {
                Tptr=Tptr->map_child[key[level]];
            }
            else
            {
                return false;
            }
        }
        if(Tptr->_isLeaf==true)
            return true;
        return false;
    }

    void PrintAllWords()
    {   //print all words in trie in dictionary order
        struct TrieTree *Tptr =_root;
        if(Tptr->map_child.empty())
            {
                std::cout<<"Trie is Empty"<<std::endl;
                return;
            }

        printWords(Tptr,"");

    }
    void PrintAllWords(std::string pre)
    {   //print all words in trie with prefix pre in Dictionary order
        struct TrieTree *Tptr =_root;
        if(Tptr->map_child.empty())
            {
                std::cout<<"Trie is Empty"<<std::endl;
                return;
            }

        for(int i=0;i<pre.length();i++)
        {
            Tptr=Tptr->map_child[pre[i]];
        }

        printWords(Tptr,pre);

    }


};

int main(){
Trie t;

std::string str;
std::fstream fs;
fs.open("words.txt",std::ios::in);

while(fs>>str){
    t.Insert(str);
}

t.PrintAllWords();

return 0;
}

我不明白输出,请查看代码并提出修复建议。谢谢

【问题讨论】:

  • 我建议在每次插入后做一些结构验证。找到破坏你的 Trie 的单词并从那里向后工作。不要只是在这里转储程序并说“请修复”。
  • @paddy 在每个插入函数返回 word count 之后。请至少在评论之前先阅读它。 map_count 还保留了带有前缀的单词的计数,这显示了正确的结果。
  • 抱歉,请至少在提问之前先进行调试。我在发表评论之前阅读了你的问题,我没有看到任何证据表明你试图深入研究这个问题。我看到的是你写了一个完整的程序,它没有工作。您实际上是在要求我们对您的代码进行静态分析以告诉您原因。有人真的花时间为你做这件事,你不幸运吗?

标签: c++ algorithm data-structures trie


【解决方案1】:

添加单词“a”时,如果树中没有以“a”开头的单词,则添加一个以“a”为值的“叶子”节点。如果您随后添加以“a”开头的单词,例如“an”,您将添加“n”节点作为“a”节点的子节点。但是,当您打印所有单词时,您会在遇到叶节点时停止递归,这意味着您会忽略以该单词开头的所有其他单词。

简单的解决方案:从printWords 中删除return。

类似地,如果树中已经有“an”,当你添加“a”时,你不会将它标记为叶子,所以它永远不会被输出。

简单的解决方案:添加单词时设置_isLeaf,即使节点已经存在(即在Insert中的else子句中添加Tptr-&gt;_isLeaf=true;

我认为您最好将 _isLeaf 更改为 _isWord 之类的东西,因为叶子节点带有子项似乎很奇怪。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-01-07
    • 2012-06-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多