【问题标题】:Logic flaw in trie searchtrie搜索中的逻辑缺陷
【发布时间】:2015-04-24 04:43:50
【问题描述】:

我目前正在为实践进行 trie 实现,但遇到了心理障碍。

问题在于我的搜索功能。我试图让我的 trie 树在加载到程序内存后能够从提供的前缀中检索字符串列表。

我也知道我可以使用队列/不应该在 C++ 等中使用 C 函数。可以说这只是一个“草稿”。

这是我目前所拥有的:

bool SearchForStrings(vector<string> &output, string data)
{
    Node *iter = GetLastNode("an");
    Node *hold = iter;
    stack<char> str;


    while (hold->visited == false)
    {
        int index = GetNextChild(iter);
        if (index > -1)
        {
            str.push(char('a' + index));
            //current.push(iter);
            iter = iter->next[index];
        }
        //We've hit a leaf so we want to unwind the stack and print the string
        else if (index < 0 && IsLeaf(iter))
        {
            iter->visited = true;
            string temp("");
            stringstream ss;

            while (str.size() > 0)
            {
                temp += str.top();

                str.pop();
            }

            int i = 0;
            for (std::string::reverse_iterator it = temp.rbegin(); it != temp.rend(); it++)
                ss << *it;

            //Store the string we have
            output.push_back(data + ss.str());
            //Move our iterator back to the root node
            iter = hold;
        }
        //We know this isnt a leaf so we dont want to print out the stack
        else
        {
            iter->visited = true;
            iter = hold;
        }


    }
    return (output.size() > 0);

}

int GetNextChild(Node *s)
{

    for (int i = 0; i < 26; i++)
    {
        if (s->next[i] != nullptr && s->next[i]->visited == false)
            return i;
    }

    return -1;
}

bool IsLeaf(Node *s)
{
    for (int i = 0; i < 26; i++)
    {
        if (s->next[i] != nullptr)
            return false;
    }

    return true;
}
struct Node{
int value;
Node *next[26];
bool visited;

};

代码太长或者我会全部发布,GetLastNode() 检索传入数据末尾的节点,所以如果前缀是 'su' 并且字符串是 'substring' 节点将是指向用作人工根节点的“u”

【问题讨论】:

  • 为什么不使用递归实现?查看键的第一个字符:如果该字符与当前节点的其中一个子节点匹配,则从键中删除 char 并递归到其中,然后将该字符添加到所有响应中。如果键为空,则递归到所有孩子,加入来自不同调用的响应。
  • 当我尝试在我的代码中编写递归函数时,我仍然很挣扎,如果要修复我已经编写的内容,我会更难做到这一点。
  • 在我原来的问题上再补充一点:我添加了以下词{“the”、“a”、“there”、“answer”、“any”、“by” , "bye", "anytime" } 进入我的特里树,然后我搜索任何以 "an" 开头的东西,我得到 {"answer", "anytime"} 的输出,但它忽略了 "any"。这应该是一个简单的修复,但它让我头疼
  • 好的,删除了我的答案,因为我相信它会遇到和你一样的问题。我认为您的问题是IsLeaf() 您的结构无法区分没有条目的节点(即a-n-y- 前缀到anywhere)和具有条目a-n-y 的节点

标签: c++ tree trie


【解决方案1】:

(可能完全错误...只是在这里输入,没有测试) 类似:

首先,我们需要一种方式来表示一个节点代表一个条目。

那么让我们来吧:

struct Node{
    int value;
    Node *next[26];
    bool entry;
};

我已经删除了你的访问标志,因为我没有用它。

你应该修改你的插入/更新/删除函数来支持这个标志。如果标志为真,则表示该节点存在实际条目。

现在我们可以修改

bool isLeaf(Node *s) {
    return s->entry;
}

意味着当有条目时我们会考虑叶子...现在可能名称是错误的,因为 叶子 可能有子节点(“y”节点带有“any”和“anywhere”是叶子,但它有孩子)

现在开始搜索:

首先是一个可以调用的公共函数。

bool searchForStrings(std::vector<string> &output, const std::string &key) {
    // start the recursion
    // theTrieRoot is the root node for the whole structure
    return searchForString(theTrieRoot,output,key);
}

然后是用于递归的内部函数。

bool searchForStrings(Node *node, std::vector<string> &output, const std::string &key) {
    if(isLeaf(node->next[i])) {
        // leaf node - add an empty string.
        output.push_back(std::string());
    } 

    if(key.empty()) {
        // Key is empty, collect all child nodes.
        for (int i = 0; i < 26; i++)
        {
            if (node->next[i] != nullptr) {
                std::vector<std::string> partial;
                searchForStrings(node->next[i],partial,key);
                // so we got a list of the childs,
                // add the key of this node to them.                
                for(auto s:partial) {
                    output.push_back(std::string('a'+i)+s)
                }
           }
       } // end for
   } // end if key.empty
   else {
       // key is not empty, try to get the node for the
       // first character of the key.
       int c=key[0]-'a';
       if((c<0 || (c>26)) {
           // first character was not a letter.
           return false;
        }
        if(node->next[c]==nullptr) {
           // no match (no node where we expect it)
           return false;
        }
        // recurse into the node matching the key
        std::vector<std::string> partial;
        searchForStrings(node->next[c],partial,key.substr(1));
        // add the key of this node to the result
        for(auto s:partial) {            
            output.push_back(std::string(key[0])+s)
        }            
    }
    // provide a meaningful return value
    if(output.empty()) {
       return false;
    } else {
       return true;
    }
}

“an”搜索的执行是。

  • 调用 searchForStrings(root,[],"an")
    • 根不是叶子,键不是空的。匹配下一个以“a”为键的节点
    • 调用 searchForStrings(node(a),[],"n")
      • node(a) 不是叶子,key 不为空。匹配下一个以“n”为键的节点
      • 调用 searchForStrings(node(n),[],"")
        • node(n) 不是叶子,key 是空的。需要递归所有非空子:
        • 调用 searchForStrings(node(s),[],"")
          • node(s) 不是叶子,key 为空,需要递归所有非空子节点:
          • ...最终我们会到达Node(r),它是一个叶子节点,所以它会返回一个[""],返回它会被添加["r"] -> ["er"] -> ["wer"] -> ["swer"]
        • 调用 searchForStings(node(y),[],"")
          • node(y) 是叶子(在输出中添加“”),键为空,
          • 递归,我们会得到["time"]
          • 我们将返回 ["","time"]
        • 此时我们将添加“y”得到["y","ytime"]
      • 在这里我们将添加“n”以得到 ["nswer","ny","nytime"]
    • 添加“a”以获得 ["answer","any","anytime"]
  • 我们完成了

【讨论】:

  • 你从哪里得到变量“i”:searchForStrings(root->next[i],partial,key.substr(1));
  • @RuneaterDaWizKid,已修复:应该是 c
  • @RuneaterDaWizKid,重新设计了递归实现,提供了更多细节并解释了它的工作原理。
猜你喜欢
  • 2017-07-16
  • 2014-10-18
  • 2015-09-08
  • 2020-09-28
  • 2015-09-25
  • 1970-01-01
  • 2015-10-19
  • 1970-01-01
  • 2021-08-25
相关资源
最近更新 更多