正确退出递归?
Properly exiting out of recursions?
TrieNode 和 Trie 对象:
struct TrieNode {
char nodeChar = NULL;
map<char, TrieNode> children;
TrieNode() {}
TrieNode(char c) { nodeChar = c; }
};
struct Trie {
TrieNode *root = new TrieNode();
typedef pair<char, TrieNode> letter;
typedef map<char, TrieNode>::iterator it;
Trie(vector<string> dictionary) {
for (int i = 0; i < dictionary.size(); i++) {
insert(dictionary[i]);
}
}
void insert(string toInsert) {
TrieNode * curr = root;
int increment = 0;
// while letters still exist within the trie traverse through the trie
while (curr->children.find(toInsert[increment]) != curr->children.end()) { //letter found
curr = &(curr->children.find(toInsert[increment])->second);
increment++;
}
//when it doesn't exist we know that this will be a new branch
for (int i = increment; i < toInsert.length(); i++) {
TrieNode temp(toInsert[i]);
curr->children.insert(letter(toInsert[i], temp));
curr = &(curr->children.find(toInsert[i])->second);
if (i == toInsert.length() - 1) {
temp.nodeChar = NULL;
curr->children.insert(letter(NULL, temp));
}
}
}
vector<string> findPre(string pre) {
vector<string> list;
TrieNode * curr = root;
/*First find if the pre actually exist*/
for (int i = 0; i < pre.length(); i++) {
if (curr->children.find(pre[i]) == curr->children.end()) { //DNE
return list;
}
else {
curr = &(curr->children.find(pre[i])->second);
}
}
/*Now curr is at the end of the prefix, now we will perform a DFS*/
pre = pre.substr(0, pre.length() - 1);
findPre(list, curr, pre);
}
void findPre(vector<string> &list, TrieNode *curr, string prefix) {
if (curr->nodeChar == NULL) {
list.push_back(prefix);
return;
}
else {
prefix += curr->nodeChar;
for (it i = curr->children.begin(); i != curr->children.end(); i++) {
findPre(list, &i->second, prefix);
}
}
}
};
这个函数有问题:
void findPre(vector<string> &list, TrieNode *curr, string prefix) {
/*if children of TrieNode contains NULL char, it means this branch up to this point is a complete word*/
if (curr->nodeChar == NULL) {
list.push_back(prefix);
}
else {
prefix += curr->nodeChar;
for (it i = curr->children.begin(); i != curr->children.end(); i++) {
findPre(list, &i->second, prefix);
}
}
}
目的是 return 使用 DFS 的 trie 中具有相同前缀的所有单词。我设法检索了所有必要的字符串,但我无法退出递归。
代码完成 if 语句的最后一次迭代并中断。 Visual Studio 没有 return 任何错误代码。
递归的典型结束就像你说的- return
所有单词。标准递归看起来像这样:
returnType function(params...){
//Do stuff
if(need to recurse){
return function(next params...);
}else{ //This should be your defined base-case
return base-case;
}
问题在于您的递归函数永远无法 return- 它可以执行 push_back
,或者可以再次调用自身。这些似乎都没有正确退出,所以它要么安静地结束(推断 return 什么都没有),要么继续递归。
在您的情况下,您可能需要将递归的结果存储在列表等中间结构中,然后在迭代后 return 该列表(因为它是树搜索并且应该检查所有children,不是 return 第一个)
关于这一点,您似乎遗漏了递归的一部分——递归的存在是为了满足一个目的:将问题分解成多个部分,直到这些部分变得微不足道。然后 return 该案例并构建回完整的解决方案。任何 tree-searching 都必须来自这个基本结构,否则您可能会错过一些东西 - 比如忘记 return
您的结果。
检查您的 Trie 结构的完整性。该功能似乎是正确的。它不会终止的原因是如果一个或多个叶节点没有 curr->nodeChar == NULL。
另一种情况是任何节点(叶子或非叶子)都有一个垃圾子节点。这将导致递归中断读取垃圾值并且没有理由停止。 运行 在调试模式下应该会因分段错误而中断执行。
编写另一个函数来测试是否所有叶节点都以 NULL 终止。
编辑:
贴出代码后,楼主已经指出问题是he/she没有返回字符串列表。
除此之外,我还想根据代码提供一些建议:
如果 toInsert
字符串已经在 Trie 中,这个 while 循环如何终止。
您将超出 toInsert
字符串并读取垃圾字符。
之后它会退出,但读取超出字符串的范围是一种糟糕的编程方式。
// while letters still exist within the trie traverse through the trie
while (curr->children.find(toInsert[increment]) != curr->children.end())
{ //letter found
curr = &(curr->children.find(toInsert[increment])->second);
increment++;
}
可以这样写:
while (increment < toInsert.length() &&
curr->children.find(toInsert[increment]) != curr->children.end())
此外,
Trie( vector<string> dictionary)
应该是
Trie( const vector<string>& dictionary )
因为字典可以是一个大对象。如果您不通过引用传递,它将创建第二个副本。这效率不高。
我是个白痴。我忘了 return 列出第一个 findPre() 函数。
vector<string> findPre(string pre) {
vector<string> list;
TrieNode * curr = root;
/*First find if the pre actually exist*/
for (int i = 0; i < pre.length(); i++) {
if (curr->children.find(pre[i]) == curr->children.end()) { //DNE
return list;
}
else {
curr = &(curr->children.find(pre[i])->second);
}
}
/*Now curr is at the end of the prefix, now we will perform a DFS*/
pre = pre.substr(0, pre.length() - 1);
findPre(list, curr, pre);
return list; //<----- this thing
}
TrieNode 和 Trie 对象:
struct TrieNode {
char nodeChar = NULL;
map<char, TrieNode> children;
TrieNode() {}
TrieNode(char c) { nodeChar = c; }
};
struct Trie {
TrieNode *root = new TrieNode();
typedef pair<char, TrieNode> letter;
typedef map<char, TrieNode>::iterator it;
Trie(vector<string> dictionary) {
for (int i = 0; i < dictionary.size(); i++) {
insert(dictionary[i]);
}
}
void insert(string toInsert) {
TrieNode * curr = root;
int increment = 0;
// while letters still exist within the trie traverse through the trie
while (curr->children.find(toInsert[increment]) != curr->children.end()) { //letter found
curr = &(curr->children.find(toInsert[increment])->second);
increment++;
}
//when it doesn't exist we know that this will be a new branch
for (int i = increment; i < toInsert.length(); i++) {
TrieNode temp(toInsert[i]);
curr->children.insert(letter(toInsert[i], temp));
curr = &(curr->children.find(toInsert[i])->second);
if (i == toInsert.length() - 1) {
temp.nodeChar = NULL;
curr->children.insert(letter(NULL, temp));
}
}
}
vector<string> findPre(string pre) {
vector<string> list;
TrieNode * curr = root;
/*First find if the pre actually exist*/
for (int i = 0; i < pre.length(); i++) {
if (curr->children.find(pre[i]) == curr->children.end()) { //DNE
return list;
}
else {
curr = &(curr->children.find(pre[i])->second);
}
}
/*Now curr is at the end of the prefix, now we will perform a DFS*/
pre = pre.substr(0, pre.length() - 1);
findPre(list, curr, pre);
}
void findPre(vector<string> &list, TrieNode *curr, string prefix) {
if (curr->nodeChar == NULL) {
list.push_back(prefix);
return;
}
else {
prefix += curr->nodeChar;
for (it i = curr->children.begin(); i != curr->children.end(); i++) {
findPre(list, &i->second, prefix);
}
}
}
};
这个函数有问题:
void findPre(vector<string> &list, TrieNode *curr, string prefix) {
/*if children of TrieNode contains NULL char, it means this branch up to this point is a complete word*/
if (curr->nodeChar == NULL) {
list.push_back(prefix);
}
else {
prefix += curr->nodeChar;
for (it i = curr->children.begin(); i != curr->children.end(); i++) {
findPre(list, &i->second, prefix);
}
}
}
目的是 return 使用 DFS 的 trie 中具有相同前缀的所有单词。我设法检索了所有必要的字符串,但我无法退出递归。
代码完成 if 语句的最后一次迭代并中断。 Visual Studio 没有 return 任何错误代码。
递归的典型结束就像你说的- return
所有单词。标准递归看起来像这样:
returnType function(params...){
//Do stuff
if(need to recurse){
return function(next params...);
}else{ //This should be your defined base-case
return base-case;
}
问题在于您的递归函数永远无法 return- 它可以执行 push_back
,或者可以再次调用自身。这些似乎都没有正确退出,所以它要么安静地结束(推断 return 什么都没有),要么继续递归。
在您的情况下,您可能需要将递归的结果存储在列表等中间结构中,然后在迭代后 return 该列表(因为它是树搜索并且应该检查所有children,不是 return 第一个)
关于这一点,您似乎遗漏了递归的一部分——递归的存在是为了满足一个目的:将问题分解成多个部分,直到这些部分变得微不足道。然后 return 该案例并构建回完整的解决方案。任何 tree-searching 都必须来自这个基本结构,否则您可能会错过一些东西 - 比如忘记 return
您的结果。
检查您的 Trie 结构的完整性。该功能似乎是正确的。它不会终止的原因是如果一个或多个叶节点没有 curr->nodeChar == NULL。
另一种情况是任何节点(叶子或非叶子)都有一个垃圾子节点。这将导致递归中断读取垃圾值并且没有理由停止。 运行 在调试模式下应该会因分段错误而中断执行。
编写另一个函数来测试是否所有叶节点都以 NULL 终止。
编辑:
贴出代码后,楼主已经指出问题是he/she没有返回字符串列表。
除此之外,我还想根据代码提供一些建议:
如果 toInsert
字符串已经在 Trie 中,这个 while 循环如何终止。
您将超出 toInsert
字符串并读取垃圾字符。
之后它会退出,但读取超出字符串的范围是一种糟糕的编程方式。
// while letters still exist within the trie traverse through the trie
while (curr->children.find(toInsert[increment]) != curr->children.end())
{ //letter found
curr = &(curr->children.find(toInsert[increment])->second);
increment++;
}
可以这样写:
while (increment < toInsert.length() &&
curr->children.find(toInsert[increment]) != curr->children.end())
此外,
Trie( vector<string> dictionary)
应该是
Trie( const vector<string>& dictionary )
因为字典可以是一个大对象。如果您不通过引用传递,它将创建第二个副本。这效率不高。
我是个白痴。我忘了 return 列出第一个 findPre() 函数。
vector<string> findPre(string pre) {
vector<string> list;
TrieNode * curr = root;
/*First find if the pre actually exist*/
for (int i = 0; i < pre.length(); i++) {
if (curr->children.find(pre[i]) == curr->children.end()) { //DNE
return list;
}
else {
curr = &(curr->children.find(pre[i])->second);
}
}
/*Now curr is at the end of the prefix, now we will perform a DFS*/
pre = pre.substr(0, pre.length() - 1);
findPre(list, curr, pre);
return list; //<----- this thing
}