使用Queue插入到n元树中
Insert into n-ary tree using Queue
我有一个简单的n-ary(最多3个子节点),其中插入的第一个节点将是根节点。在添加任何其他节点之前,我必须搜索树,并在满足条件的情况下将先前插入的节点作为子节点插入。我的插入方法对于第一次插入和随后的插入是重载的。
我可以使用以下方法插入第一个节点:
void Tree::AddSkill(char* name, char* desc, int level)
{
Skill s(name, desc, level);
Node * newNode = new Node(s);
//newNode->aSkill = Skill(name, desc, level);
newNode->parent = NULL;
for (int i = 0; i<CHILD_MAX; i++)
{
newNode->children[i] = NULL;
}
if (this->root == NULL)
{
this->root = newNode;
}
else
{
this->root->parent = newNode;
newNode->children[0] = this->root;
this->root = newNode;
}
}
我有一些问题,随后插入到树,下面是我到目前为止的代码:
void Tree::AddSkill(char* name, char* desc, int level, char* parentName)
{
if (this->root == NULL)
{
cout << "Error: no nodes in tree.n";
return;
}
Node* node = NULL;
Skill s(name, desc, level);
Node * child = new Node(s);
while (root != NULL)
{
if (strcmp(child->aSkill.GetName(), parentName) == 0)
{
for (int i = 0; i < CHILD_MAX; i++)
{
if (node->children[i] == NULL)
{
child->aSkill = s;
child->parent = node;
node->children[i] = child;
return;
}
}
}
}
}
当我通过VS调试器运行代码时,第二个AddSkill
方法中的while循环会不断重复。我不太确定我做错了什么,或者我需要实现什么概念,任何帮助都会很感激。
注:这是一个作业(不确定合适的标签是什么)。
更新:我尝试使用队列实现过载的AddSkill()
。这是我试过的。
void SkillTree::AddSkill(char* name, char* desc, int level, char* parentName)
{
if (this->root == NULL)
{
cout << "Error: no nodes in tree.n";
return;
}
queue<Node*> q;
q.push(this->root);
while (!q.empty())
{
Node * n = q.front();
q.pop();
if (strcmp(n->aSkill.GetName(), parentName) == 0)
{
for (int i = 0; i<CHILD_MAX; i++)
{
if (n->children[i] == NULL)
{
Skill s(name, desc, level);
Node * child = new Node(s);
//When I comment out the next 3 lines, program does not crash. Not sure what the problem is here.
child->aSkill = s;
child->parent = n;
n->children[i] = child;
return;
}
}
return;
}
for (int i = 0; i<CHILD_MAX; i++)
{
if (n->children[i] != NULL)
{
q.push(n->children[i]);
}
}
}
}
技能等级
#include <iostream>
#include "Skill.h"
Skill::Skill()
{
name = NULL;
desc = NULL;
level = 0;
}
Skill::Skill(char* name, char* desc, int level) : level(level), name(new char[strlen(name) + 1]), desc(new char[strlen(desc) + 1])
{
strcpy_s(this->name, (strlen(name) + 1), name);
strcpy_s(this->desc, (strlen(desc) + 1), desc);
}
Skill::Skill(const Skill& aSkill)
{
this->name = new char[strlen(aSkill.name) + 1];
strcpy_s(this->name, (strlen(aSkill.name) + 1), aSkill.name);
this->level = aSkill.level;
this->desc = new char[strlen(aSkill.desc) + 1];
strcpy_s(this->desc, (strlen(aSkill.desc) + 1), aSkill.desc);
}
Skill& Skill::operator=(const Skill& aSkill)
{
if (this == &aSkill)
return *this;
else
{
delete[] name;
delete[] desc;
name = new char[strlen(aSkill.name) + 1];
strcpy_s(name, (strlen(aSkill.name) + 1), aSkill.name);
desc = new char[strlen(aSkill.desc) + 1];
strcpy_s(name, (strlen(aSkill.desc) + 1), aSkill.desc);
level = aSkill.level;
return *this;
}
}
Skill::~Skill()
{
delete[] name;
delete[] desc;
}
char* Skill::GetName() const
{
return name;
}
char* Skill::GetDesc() const
{
return desc;
}
int Skill::GetLevel() const
{
return level;
}
void Skill::Display(ostream& out)
{
out << "- " << GetName() << " -- " << GetDesc() << " [Lvl: " << GetLevel() << "]n";
}
节点: Skill aSkill;
Node* parent;
Node* children[CHILD_MAX];
Node() : parent(NULL)
{
for (int i = 0; i < CHILD_MAX; i++)
{
children[i] = NULL;
}
};
Node(const Skill& n) : aSkill(n), parent(NULL)
{
for (int i = 0; i < CHILD_MAX; i++)
{
children[i] = NULL;
}
};
这是main()
的摘录
SkillTree student("Student");
student.Display(cout);
student.AddSkill("Alphabet","Mastery of letters and sounds",0);
student.Display(cout);
student.AddSkill("Reading","The ability to read all manner of written material",1,"Alphabet");
student.AddSkill("Writing","The ability to put your thoughts on paper",1,"Alphabet");
student.Display(cout);
student.AddSkill("Speed Reading Level 1","Read any text twice as fast as normal",5,"Reading");
student.AddSkill("Speed Reading Level 2","Read any text four times as fast as normal",10,"Speed Reading Level 1");
student.AddSkill("Memorization","Memorize average sized texts",10,"Reading");
student.AddSkill("Massive Memorization","Memorize large sized texts",20,"Memorization");
student.AddSkill("Spell Writing","The ability to write spells",5,"Writing");
student.AddSkill("History","The ability to write (and rewrite) history",10,"Writing");
student.AddSkill("Written Creation","The ability to write things into reality",20,"History");
student.Display(cout);
student.Display(cout);
调用的两个函数如下
void Tree::Display(ostream& out)
{
out << "Skill Tree: " << title << "n";
if (this->root == NULL)
{
cout << "Emptyn";
return;
}
else
Display_r(out, this->root, 1);
}
void Tree::Display_r(ostream& out, Node* n, int depth)
{
for (int i = 0; i<depth; i++)
{
out << " ";
}
n->aSkill.Display(out);
for (int i = 0; i<CHILD_MAX; i++)
{
if (n->children[i] != NULL)
{
Display_r(out, n->children[i], depth + 1);
}
}
}
如果我注释掉AddSkill()
的队列实现中的一段代码,我没有得到错误。
在第一个AddSkill()
中,您将新节点插入到树的顶部,使其成为新的根。
在第二个AddSkill()
中,您打算插入新节点作为父技能的子节点。方法似乎是:
- 检查树中至少有一个节点(初始if)
- 遍历树查找父节点(
while
循环) - 如果找到父类,找到第一个空的子类来插入新技能(内部
for
循环)
有哪些问题
你的算法有几个缺陷:
- 你的循环在
root
不为空。由于这里的树不是空的,并且您没有删除任何节点,因此该条件将保持为真,从而允许无限循环。 - ,然后检查新子名称是否与父名称对应。我假设在大多数情况下这将是假的(否则您将需要少一个参数)。所以这将确保循环是无止境的。
- 之后假定
node
是当前节点,并将新的child
插入node
的子节点。此代码不执行。幸运的是:这将是未定义的行为,因为您已经将node
设置为NULL
,并且从未更改此值。
如何解决
要做到这一点,您必须从root
处的node
开始,然后检查节点的名称是否与parentname
匹配,如果是,就像之前一样插入子节点。
还有最后一个问题。一个相当重要的问题。你的算法结构适用于链表遍历,但不适用于树遍历。树遍历算法要么需要堆栈/列表来跟踪要探索的所有分支,要么需要递归方法。
这里有一些代码(对不起,我用string
代替了char*
,并使用vector<Node*>
代替Node*[]
),使用AddSkill()
的辅助过载来执行递归搜索:
// replaces the former one that you had
void Tree::AddSkill(string name, string desc, int level, string parentName)
{
if (root == NULL)
{
cout << "Error: no nodes in tree.n";
return;
}
Skill s(name, desc, level);
AddSkill(root, s, parentName);
}
// auxiliary helper
void Tree::AddSkill(Node*node, Skill& s, string& parentName)
{
if (node->sk.name == parentName) { // if found, add the new node as childen
Node * child = new Node(s);
child->parent = node;
node->children.push_back(child);
}
else {
for (auto &x : node->children) // for all the children
AddSkill(x, s, parentName); // search recursively
}
}
这里是一个使用共享指针代替原始指针的在线演示
- 使用C++库在Android项目中修改gradle中的cmake参数,用于插入指令的测试
- 有关插入适配器的错误。[错误]请求从 'back_insert_iterator<vector<>>' 类型转换为非标量类型
- 预处理器:插入结构名称中的前一个行号
- 在未初始化映射的情况下,将值插入到映射的映射中
- 如何在c++中只将键插入到bimap的一侧
- 如何将结构插入到集合中并打印集合的成员
- C++json插入数组
- Visual Studio 2019:插入多个C++风格的单行注释
- nlohmann-json将一个数组插入到另一个数组中
- 有效地使用std::unordered_map来插入或增加键的值
- 为字符串中每 N 个字符插入空格的函数没有按照我认为的方式工作?
- 正在插入动态数组
- 插入或删除时获取usb的dos_name
- 叮叮当当在修复时插入多个"覆盖"说明符
- 链表c++插入,所有情况都已检查,但没有任何工作
- 将重物插入std::map
- C++17 - 使用自定义分配器的节点提取/重新插入 - 适用于 clang++/libc++,但不适用于 libstd
- 在数字之间插入 + 或 - 符号以使其等于整数
- 在字符串中插入空格
- 使用Queue插入到n元树中