英文:
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<TrieNode*, 26> child;
TrieNode() : isEnd(false), child{} {}
bool isEmpty() {
for (int i = 0; i < 26; i++) {
if (this->child[i] != nullptr) {
return false;
}
}
return true;
}
};
TrieNode* root;
Trie () {
root = new TrieNode();
}
void insert (string &word) {
TrieNode* curr = root;
for (int i = 0; i < word.length(); i++) {
int idx = word[i] - 'a';
if (curr->child[idx] == nullptr) {
curr->child[idx] = new TrieNode();
}
curr = curr->child[idx];
}
curr->isEnd = true;
}
bool isPre (string &word, int type) { // type 0 : isPresent, type 1: isPrefix
TrieNode* curr = root;
for (int i = 0; i < word.length(); i++) {
int idx = word[i] - 'a';
if (curr->child[idx] == nullptr) {
return false;
}
curr = curr->child[idx];
}
return (type ? true : curr->isEnd);
}
TrieNode* erase (TrieNode* curr, string &word, int i) {
if (curr == nullptr) {
return nullptr;
}
if (i == word.length()) {
curr->isEnd = false;
if (curr->isEmpty()) {
delete curr;
curr = nullptr;
}
return curr;
}
int idx = word[i] - 'a';
curr->child[idx] = erase(curr->child[idx], word, i + 1);
if (curr->isEnd == false && curr->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).
/* Trie Struct */
string str = "a";
Trie trie;
cout << trie.isPre(str, 0); // Working Fine
trie.insert(str);
cout << trie.isPre(str, 0); // Working Fine
trie.erase(trie.root, str, 0);
cout << 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 > 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 &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;
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论