使用矢量的C ++二叉树

时间:2018-05-05 06:51:51

标签: c++ vector data-structures binary-tree binary-search-tree

我的任务是在向量中存储二叉树。在每个节点中存储一个int ID,int Age和一个字符串名称。

节点通过ID在矢量中存储和组织。

在向量中存储二叉树时,我使用算法2i和2i + 1分别指示节点的左右孩子。

我设法创建了一个插入方法,我相信它满足这些条件并将一个节点插入到一个向量中,但是,在尝试将这些节点插入到向量中之后

50 21蒂姆

75 22史蒂夫

我注意到它实际上并没有将这些节点插入到矢量中。

我在插入后放置了一行打印索引,我意识到在第一次插入后,索引不再更新。

示例使用此示例运行插入方法

enter image description here

我的insert()方法有问题吗?

这是我的代码。

#include "BinaryTree.h"
#include <string>
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;
int index = 0;

struct Node
{
    int ID;
    int age;
    string name;

    Node()
    {

    }

    Node(int id, int Age, string nm)
    {
        this->ID = id;
        this->age = Age;
        this->name = nm;
    }
};

vector<Node> binaryTree(30);


BST::BST()
{

}



void BST::start()
{
    int choice;


    cout << "What would you like to do?" << endl;
    cout << "1. Add a node to the tree" << endl;
    cout << "2. Delete a node from the tree" << endl;
    cout << "3. Find a node in the tree" << endl;
    cout << "4. Report the contents of the tree" << endl;
    cout << "5. Exit program" << endl;

    cin >> choice;

    if (choice == 1)
    {
        insert();
    }

    if (choice == 2)
    {
        Delete();
    }

    if (choice == 3)
    {
        find();
    }

    if (choice == 4)
    {
        report();
    }


}


void BST::insert()
{
    int ID;
    int AGE;
    string NAME;
    int root = 1;

    bool success = false;
    cout << "Please enter the ID number, age and name:" << endl;

    do
    {
        cin >> ID >> AGE >> NAME;
    } while (ID < 0);

    Node *tree = new Node(ID, AGE, NAME);


    if (index == 0)
    {
        binaryTree[1] = *tree;
    }

    if (index > 0)
    {
        do
        {
            if (tree->ID > binaryTree.at(root).ID)
            {
                root = 2 * root + 1;

            }

            if (tree->ID < binaryTree.at(root).ID)
            {
                root = 2 * root;
            }

            if (binaryTree.at(root).ID == NULL)
            {
                binaryTree.at(root) = *tree;
                cout << "Added!" << endl;
                cout << "Vector size: " << binaryTree.size() << endl;
                success = true;
            }
        } while (!success);
    }

    index++;
    cout << index << endl;
    delete tree;

    start();
}
编辑:我意识到我没有检查索引,因此我将其从'='更改为'=='比较以启动循环。但是,现在我得到一个_Xout_of_range(“无效的向量下标”);向量类抛出的异常

这是错误。

enter image description here

1 个答案:

答案 0 :(得分:1)

do
{
    cin >> ID >> AGE >> NAME;
} while (ID < 0);

不是实际问题,但您应该在此操作后检查std::cin的状态 - 如果用户确实输入了无效输入("xyz"),则cin仍处于失败状态且您永远不会得到有效的输入...此外,如果你使id和age无符号,你不必检查负输入。

我的个人变体:

for(;;)
{
    std::cin >> id >> age >> name;
    if(std::cin)
        // input is valid!
        break;
    std::cout << "invalid input" << std::endl; // some better text?
    std::cin.clear();
    std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}

有关详细信息,请参阅this answer

if (index = 0)
//        ^ this is an assignment! always sets index to 0 and then
//          checks the value; for COMPARISON, you need to use ==
{
    binaryTree[1] = *tree;
//             ^ and when do you ever fill index 0???
}

分配而不是比较是实际导致问题的原因!

如果index代表向量中的元素数量,则可以完全删除它,使用binaryTree.size()代替...

if (binaryTree.at(root).ID == NULL)
{
    binaryTree.at(root) = *tree;
    cout << "Vector size: " << binaryTree.size() << endl;
}

等等,你打算用默认值预先填充向量吗?如果是这样,binaryTree.size()将总是具有相同的大小......并且您可能需要一个巨大的预分配数组,并且可能会得到较差的填充等级。回来以后。请注意NULL是一个代表空指针的宏(如果你真的在处理,请改为使用nullptr关键字!)。不要将它用于整数比较,直接使用值0

另外:如果向量不够大,at将抛出异常。然后?首选使用索引运算符,但之前检查向量是否足够大。如果不是,请适当增加矢量的大小(例如,将其设置为之前大小的两倍)。

Node* tree = new Node(id, age, name);
binaryTree[x] = *tree;
delete tree;

那么,为什么然后在堆上?只需这样做:

Node             tree(id, age, name);
//  no pointer!  creating object directly on the stack!
binaryTree[x] = tree; // assign directly
// no delete necessary, object will be destructed automatically when leaving scope

在当前不需要的情况下,移动和复制结果相同,但是对于更复杂的对象,移动而不是复制可能有值,因此您可以优化到:

binaryTree[x] = std::move(tree); // creates an rvalue reference...

回到size / fillgrade问题:二叉树只有保持平衡才有效。通常,在运行时,如果存储在像数组之类的结构中,也可以存储在内存中。所以你应该保持你的树平衡。变量可能不是从树的根开始,而是简单地在末尾附加元素(使用push_back)然后向上移动它,只要它比父节点大,如果在左侧树中,或者小于,如果在正确的树中。如果提供移动构造函数,则可以使用std::swap来交换元素。这种替代方法还避免了对标记值的需要(node.id == 0)......

编辑以回复您的评论:

好的,现在首先让算法中包含索引0,不需要跳过位置0。

然后,您的问题可能已经是第一次插入:您确定向量确实包含两个虚拟元素吗?更好的是我们从一开始就考虑我们有一个空向量。然后我们就可以做到:

unsigned int index = 0;
// renaming root to index, assuming we dropped
// the other index variable already
// benefit: you can clearly differentiate my and your code...

Node node(id, age, name);
for(;;)
{
    if(index <= binaryTree.size()) // covers empty vector, too, as 0 <= 0...
    {
        binaryTree.resize(index + 1);
        // vector with size of n can store n elements
        // at indices [0 .. n - 1] - deduce the inverse yourself
        // and you know why index + 1...
        binaryTree[index] = node;
        // if we needed to resize, the new elements are unoccupied
        // anyway, so we simply can insert and are done...
        break;
    }
    if(binaryTree[index].id == 0)
    {
        // sentinel found, can insert here:
        binaryTree[index] = node;
        break;
    }
    // now calculate the child index just as before
}

下一个子索引的计算也有错误:

 if (tree->ID > binaryTree.at(root).ID)
     root = 2 * root + 1;

 if (tree->ID < binaryTree.at(root).ID)
     root = 2 * root;

那么,如果ids是一样的???其中至少有一个必须包含相等,否则你将陷入无限循环。但是,一个条件与另一个条件完全相反,所以你可以简单地使用if / else:

 if (node.id > binaryTree[index].id)
     index = 2 * index + 1;
 else
     index = 2 * index;

现在有一些很好的调整:在C ++中,比较总是得到布尔值,然后提升为(无符号)int总是得到0或1,所以你可以,如果你愿意,可以将上面的内容缩短到下面的单行(井) ,我的重命名再次适用......):

 index = 2 * index + (tree->ID > binaryTree[index].ID);