【问题标题】:C++ Binary Tree Using Vector使用向量的 C++ 二叉树
【发布时间】:2018-10-15 15:09:45
【问题描述】:

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

节点按 ID 在向量中存储和组织。

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

我已经设法创建了一个我认为满足这些条件并将节点插入向量的插入方法,但是,在尝试将这些节点插入向量之后

50 21 蒂姆

75 22 史蒂夫

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

我放了一行插入后打印索引,发现第一次插入后,索引不再更新。

一个例子使用这个例子运行插入方法

我的 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("invalid vector subscript");向量类抛出的异常

这是错误。

【问题讨论】:

  • if (index = 0) 应该是:if (index == 0)
  • @Ian4264 哦,我什至没有注意到这一点。谢谢!但现在我得到 _Xout_of_range("invalid vector subscript");矢量类引发的错误。
  • 使用调试来确定问题出在哪里。
  • insert的最后两行很奇怪,为什么要删除tree,然后下一轮再访问,为什么又要调用start,递归太多,最终会出问题导致堆栈溢出
  • @Ian4264 我不太确定如何创建一个新节点并将其添加到树中而不会导致内存泄漏。

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


【解决方案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...

回到大小/填充等级问题:二叉树只有在保持平衡的情况下才有效。通常,在运行时,如果存储在类似数组的结构(如向量)中,也会存储在内存中。所以你应该保持你的树平衡。一个变体可能不是从树的根开始,而是简单地在末尾附加元素(使用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;

如果 id 相同呢?其中至少一个也必须包含相等性,否则您将陷入无限循环。但是,一个条件正好与另一个条件相反,所以你可以简单地使用 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);

【讨论】:

  • 不幸的是,即使将索引更改为 '==' 而不是 '=' 我的问题仍然存在。我什至尝试在每个 for 循环中执行 binaryTree.resize(root*2 +1) ,但是我仍然得到相同的结果!我想把头发扯下来!
  • 想象一下如果有人按排序顺序插入节点会发生什么,例如。 G。 1, 2, 3, 4. 你的树将退化为一个简单的链表 - 但是就向量而言,只有索引 2^n+1 处的位置将被占用,所有其他将被占用保持空白。你真的愿意接受吗?如果是这样,我们可以进一步研究,否则,我们最好切换到替代实现......
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-11-13
  • 2022-01-11
  • 1970-01-01
  • 1970-01-01
  • 2011-07-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多