简体   繁体   English

使用Queue插入n元树

[英]Insert into n-ary tree using Queue

I have a simple n-ary (3 child nodes maximum) whereby the first node inserted will be the root. 我有一个简单的n元(最多3个子节点),由此插入的第一个节点将是根节点。 Before, I add any other node, I have to search the tree and insert as a child node from a previously inserted node, if a condition is meet. 在添加任何其他节点之前,如果满足条件,我必须搜索树并从先前插入的节点作为子节点插入。 My insertion methods is overloaded for first insertion and subsequent insertions. 我的插入方法在第一次插入和随后的插入时过载。

I was able to insert the first node using this method: 我能够使用此方法插入第一个节点:

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;
    }
}

I'm having a few issues with subsequent insertion into the tree, Here is the code I have so far: 我在随后插入树中遇到一些问题,这是到目前为止的代码:

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;
                }
            }
        }
    }
}

When I run the code through VS Debugger, the while loop in the second AddSkill method repeats endlessly. 当我通过VS Debugger运行代码时,第二个AddSkill方法中的while循环不断重复。 I'm not so sure what I'm doing wrong or what concept I need to implement, any help will be appreciated. 我不确定自己在做什么错或需要实现什么概念,我们将不胜感激。

PS This is an Homework (Not sure what the appropriate tag is). PS这是一项作业(不确定适当的标签是什么)。

Update: I have tried to implement the overloaded AddSkill() using Queue. 更新:我尝试使用Queue实现重载的AddSkill() This is what I've tried with it. 这就是我尝试过的。

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]);
            }
        }
    }
}

Skill Class 技能课

#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";
}

Node: 节点:

    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;
        }
    };

Here is an extract from main() 这是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);

The two functions that student.Display(cout); 学生的两个函数student.Display(cout); calls are as follow 电话如下

    void Tree::Display(ostream& out)
{
    out << "Skill Tree: " << title << "\n";
    if (this->root == NULL)
    {
        cout << "Empty\n";
        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);
        }
    }
}

If I comment out a section of code in the Queue implementation of AddSkill() , I get no error. 如果我注释掉AddSkill()的Queue实现中的一段代码,则不会出错。

In the first AddSkill() you insert the new node on the top of the tree, making it the new root. 在第一个AddSkill() ,将新节点插入树的顶部,使其成为新的根。

In the second AddSkill() you intend to insert the new node as child of a parent skill. 在第二个AddSkill()您打算将新节点作为父级技能的子级插入。 The approach seems to be: 该方法似乎是:

  • check that there is at least one node in the tree (initial if) 检查树中是否至少有一个节点(如果为,则为初始值)
  • traverse the tree to find the parrent node ( while loop ) 遍历树以找到父节点( while循环)
  • if the parent is found, find the first empty child to insert the new skill (inner for loop) 如果家长发现,找到的第一个空儿插入新的技能(内for循环)

What are the problems ? 有什么问题?

THere are several flaws in your algorithm: 这是您算法中的几个缺陷:

  • you loop on root not null. 您在root循环不为null。 As the tree is not empty here, and as you don't delete any node, this condition will remain true, allowing for an endless loop. 由于此处的树不是空的,并且您不删除任何节点,因此此条件将保持为真,从而导致无限循环。
  • then you check if the new child's name corresponds to the parentname. 然后检查新孩子的名字是否对应于父母的名字。 I assume that this will be false most of the case (otherwhise you'd need one parameter less). 我认为这在大多数情况下都是错误的(否则,您将需要较少的一个参数)。 So this will ensure that the loop is endless. 因此,这将确保循环是无限的。
  • later you assume that node is the current node, and you insert the new child into node 's children. 之后,您假定该node是当前节点,然后将新的child node插入到该nodechild node This code is not exectuted. 此代码不执行。 Fortunately: it would be undefined behaviour, because you've set node to NULL and never changed this value. 幸运的是:这将是不确定的行为,因为您已将node设置为NULL并且从未更改过该值。

How to solve it ? 怎么解决呢?

To do this right, you'd have to start with node at root , then check if the node's name matches parentname , and if yes, insert the child as you did. 为此,您必须从root node开始,然后检查节点名称是否与parentname相匹配,如果是,请像您一样插入子节点。

There's a last problem however. 但是,还有最后一个问题。 A rather important one. 一个相当重要的。 The structure of your algorithm works for a linked list traversal, but not a tree traversal . 算法的结构适用于链表遍历,而不适用于树遍历 The tree traversal algorithm requires either a stack/list to keep track of all the branches to explore, or a recursive approach. 树遍历算法需要堆栈/列表来跟踪要探索的所有分支,或者需要递归方法。

Here some code ( sorry, I've replaced char* with string and used vector<Node*> instead of Node*[] ), using an auxiliary overload of AddSkill() , to perform the recursive search: 这是一些代码( 对不起,我已经用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   
    }
}

And here an online demo using shared pointers instead of raw pointers. 这里是使用共享指针而不是原始指针的在线演示

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM