如何处理C++中Trie实现中的所有删除操作

huangapple go评论62阅读模式
英文:

How to deal with all deletes in Trie Implementation in C++

问题

结构 Trie {
    结构 TrieNode {
        布尔 isEnd;
        数组 <TrieNode* 26> 孩子;
        TrieNode() : isEnd(false), 孩子{} {}
        布尔 isEmpty() {
            对于 (整数 i = 0; i < 26; i++) {
                如果 (->孩子[i] != nullptr) {
                    返回 ;
                }
            }
            返回 ;
        }
    };
    TrieNode* ;
    Trie () {
         =  TrieNode();
    }
    穿插 (字符串 &word) {
        TrieNode* 当前 = ;
        对于 (整数 i = 0; i < word.length(); i++) {
            整数 idx = word[i] - 'a';
            如果 (当前->孩子[idx] == nullptr) {
                当前->孩子[idx] =  TrieNode();
            }
            当前 = 当前->孩子[idx];
        }
        当前->isEnd = ;
    }
    布尔 isPre (字符串 &word, 整数 类型) { // 类型 0 : isPresent, 类型 1: isPrefix
        TrieNode* 当前 = ;
        对于 (整数 i = 0; i < word.length(); i++) {
            整数 idx = word[i] - 'a';
            如果 (当前->孩子[idx] == nullptr) {
                返回 ;
            }
            当前 = 当前->孩子[idx];
        }
        返回 (类型 ?  : 当前->isEnd);
    }
    TrieNode* 擦除 (TrieNode* 当前, 字符串 &word, 整数 i) {
        如果 (当前 == nullptr) {
            返回 nullptr;
        }
        如果 (i == word.length()) {
            当前->isEnd = ;
            如果 (当前->isEmpty()) {
                删除 当前;
                当前 = nullptr;
            }
            返回 当前;
        }
        整数 idx = word[i] - 'a';
        当前->孩子[idx] = 擦除(当前->孩子[idx], word, i + 1);
        如果 (当前->isEnd ==  && 当前->isEmpty()) {
            删除 当前;
            当前 = nullptr;
        }
        返回 当前;
    }
};

Note: The translation provided here is based on the code snippet you provided. If you encounter any issues or need further assistance, feel free to ask.

英文:
struct Trie {
struct TrieNode {
bool isEnd;
array&lt;TrieNode*, 26&gt; child;
TrieNode() : isEnd(false), child{} {}
bool isEmpty() {
for (int i = 0; i &lt; 26; i++) {
if (this-&gt;child[i] != nullptr) {
return false;
}
}
return true;
}
};
TrieNode* root;
Trie () {
root = new TrieNode();
}
void insert (string &amp;word) {
TrieNode* curr = root;
for (int i = 0; i &lt; word.length(); i++) {
int idx = word[i] - &#39;a&#39;;
if (curr-&gt;child[idx] == nullptr) {
curr-&gt;child[idx] = new TrieNode();
}
curr = curr-&gt;child[idx];
}
curr-&gt;isEnd = true;
}
bool isPre (string &amp;word, int type) { // type 0 : isPresent, type 1: isPrefix
TrieNode* curr = root;
for (int i = 0; i &lt; word.length(); i++) {
int idx = word[i] - &#39;a&#39;;
if (curr-&gt;child[idx] == nullptr) {
return false;
}
curr = curr-&gt;child[idx];
}
return (type ? true : curr-&gt;isEnd);
}
TrieNode* erase (TrieNode* curr, string &amp;word, int i) {
if (curr == nullptr) {
return nullptr;
}
if (i == word.length()) {
curr-&gt;isEnd = false;
if (curr-&gt;isEmpty()) {
delete curr;
curr = nullptr;
}
return curr;
}
int idx = word[i] - &#39;a&#39;;
curr-&gt;child[idx] = erase(curr-&gt;child[idx], word, i + 1);
if (curr-&gt;isEnd == false &amp;&amp; curr-&gt;isEmpty()) {
delete curr;
curr = nullptr;
}
return curr;
}
};

I implemented the following code, but the issue is with the erase function. Specifically, say I insert two elements in the trie and then remove both. Then the isPre() function stops working. I guess when I call the erase to delete the entire trie, it deletes the original root, which is then accessed, and thus the issue.

But I am not sure how to resolve it. I tried adding a nullptr check at the start of the isPre() function but even then, I get the error.

Except for the case where I delete all the trie's nodes after inserting them, the trie is working fine (even when I have not inserted anything).

Minimal Reproducible example:

/* Trie Struct */
string str = &quot;a&quot;;
Trie trie;
cout &lt;&lt; trie.isPre(str, 0); // Working Fine
trie.insert(str);
cout &lt;&lt; trie.isPre(str, 0); // Working Fine
trie.erase(trie.root, str, 0);
cout &lt;&lt; trie.isPre(str, 0); // Creates the Error

Error:

==22==ERROR: AddressSanitizer: heap-use-after-free on address 0x611000000350 at pc 0x00000034e11e bp 0x7ffdd3c379e0 sp 0x7ffdd3c379d8
READ of size 8 at 0x611000000350 thread T0

Edit:
As, I had suspected the error was caused due to the root node being delete. So, I just added a condition that root shouldn't be deleted. It worked fine. Here is the implementation. But, I am not sure if this is the way to go, I would be glad if anyone can suggest a better trie implementation

答案1

得分: 1

代码中的问题是在根节点上执行了delete操作,这是不应该发生的。一个快速修复方法是只有在i > 0时才执行delete。或者,您可以只在子节点(在递归树中向上一级)上执行delete操作,这样目标永远不会是根节点。这还意味着您不需要返回值是一个节点指针,而可以使用返回值来指示成功(如果找到了单词)或失败(没有要删除的内容):

    // 现在假定 curr 永远不会是 nullptr
    bool erase (TrieNode* curr, string &word, int i) {
        if (i == word.length()) {
            bool success = curr->isEnd;
            curr->isEnd = false;
            return success;
        }
        int idx = word[i] - 'a';
        TrieNode* child = curr->child[idx];
        bool success = child && erase(child, word, i + 1);
        if (success && !child->isEnd && child->isEmpty()) {
            delete child;
            curr->child[idx] = nullptr;
        }
        return success;
    }
英文:

Indeed your code performs a delete on the root node, which should never happen. A quick fix is to only perform the delete when i &gt; 0. Alternatively you could only perform a delete on a child (one level up in the recursion tree) -- that way the target will never be the root. This also means you don't need the return value to be a node pointer, and can instead use the return value to indicate success (if the word was found) or failure (nothing to delete):

    // Now curr is assumed to never be nullptr
bool erase (TrieNode* curr, string &amp;word, int i) {
if (i == word.length()) {
bool success = curr-&gt;isEnd;
curr-&gt;isEnd = false;
return success;
}
int idx = word[i] - &#39;a&#39;;
TrieNode* child = curr-&gt;child[idx];
bool success = child &amp;&amp; erase(child, word, i + 1);
if (success &amp;&amp; !child-&gt;isEnd &amp;&amp; child-&gt;isEmpty()) {
delete child;
curr-&gt;child[idx] = nullptr;
}
return success;
}

huangapple
  • 本文由 发表于 2023年6月5日 15:33:38
  • 转载请务必保留本文链接:https://go.coder-hub.com/76404340.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定