【问题标题】:Binary Search Tree Insert Garbage Data二叉搜索树插入垃圾数据
【发布时间】:2017-04-21 09:23:10
【问题描述】:

所以,我正在为一个项目编写一个自平衡二叉搜索树,我似乎遇到了插入函数的问题。具体来说,这里的这段代码:

if(element > n->data)
{
    n->rightChild = insert(n->rightChild, element);
}   

出于测试的目的,我只是使用 insert(root, 10) insert(root, 20) insert(root, 30) 等等。问题发生在插入 30 处。它通过并尝试用另一个非空子树替换一个非空子树(20)。我不确定为什么我似乎遇到了这个问题,因为递归是我看到它实现的唯一方法,在我发现的所有其他类似问题中,答案都是以类似的方式编码的到此或与此完全相同。谁能帮我弄清楚发生了什么?目前,在它尝试用 30 覆盖 20 子树后,它最终用垃圾数据填充根右子树,然后,当它尝试重新平衡时,抛出一个 EXC_BAD_ACCESS 错误,大概是因为我试图访问一个没有不存在。

源代码

#include <iostream>
using namespace std;

struct node
{
public:
    int data, height;
    node *leftChild, *rightChild;
};
int findMin(struct node *p) // finds the smallest node in the tree
{
    while (p->leftChild != NULL)
        p = p->leftChild;
    return p->data;
}
int findMax(struct node *p) // finds the largest node in the tree
{
    while(p->rightChild != NULL)
        p = p->rightChild;
    return p->data;
}
int max(int a, int b) // gets the max of two integers
{
    if(a > b)
        return a;
    else
        return b;
}
int height(struct node *p) // gets the height of the tree
{
    int lHeight, rHeight;
    if(p == NULL)
        return -1;
    else
    {
        lHeight = height(p->leftChild);
        rHeight = height(p->rightChild);

        if(lHeight > rHeight)
            return lHeight + 1;
        else
            return rHeight + 1;
    }
}
struct node* newNode(int element) // helper function to return a new node with empty subtrees
{
    node* newPtr = (struct node*) malloc(sizeof(newPtr));
    newPtr->data = element;
    newPtr->leftChild = NULL;
    newPtr->rightChild = NULL;
    newPtr->height = 1;
    return newPtr;
}
struct node* rightRotate(struct node* p) // function to right rotate a tree rooted at p
{
    node* child = p->leftChild;
    node* grandChild = child->rightChild;

    // perform the rotation
    child->rightChild = p;
    p->leftChild = grandChild;

    // update the height for the nodes
    p->height = max(height(p->leftChild), height(p->rightChild)) + 1;
    child->height = max(height(child->leftChild), height(child->rightChild)) + 1;

    // return new root
    return child;
}
struct node* leftRotate(struct node* p) // function to left rotate a tree rooted at p
{
    node* child = p->rightChild;
    node* grandChild = child->leftChild;

    // perform the rotation
    child->leftChild = p;
    p->rightChild = grandChild;

    // update heights
    p->height = max(height(p->leftChild), height(p->rightChild)) + 1;

    // return new root
    return child;
}

int getBalance(struct node *p)
{
    if(p == NULL)
        return 0;
    else
        return height(p->leftChild) - height(p->rightChild);
}
// recursive version of BST insert to insert the element in a sub tree rooted with root
// which returns new root of subtree
struct node* insert(struct node*& n, int element)
{
    // perform the normal BST insertion
    if(n == NULL) // if the tree is empty
        return(newNode(element));
    if(element< n->data)
    {
        n->leftChild = insert(n->leftChild, element);
    }
    if(element > n->data)
    {
        n->rightChild = insert(n->rightChild, element);
    }
    else // duplicate node
    {
        return n;
    }

    // update the height for this node
    n->height = 1 + max(height(n->leftChild), height(n->rightChild));

    // get the balance factor to see if the tree is unbalanced
    int balance = getBalance(n);

    // the tree is unbalanced, there are 4 different types of rotation to make
    // Single Right Rotation (Left Left Case)
    if(balance > 1 && element < n->leftChild->data)
    {
        return rightRotate(n);
    }
    // Single Left Rotation (Right Right Case)
    if(balance < -1 && element > n->rightChild->data)
    {
        return leftRotate(n);
    }
    // Left Right Rotation
    if(balance > 1 && element > n->leftChild->data)
    {
        n->leftChild = leftRotate(n->leftChild);
        return rightRotate(n);
    }
    // Right Left Rotation
    if(balance < -1 && element < n->rightChild->data)
    {
        n->rightChild = rightRotate(n->rightChild);
        return leftRotate(n);
    }
    cout << "Height: " << n->height << endl;
    // return the unmodified root pointer in the case that the tree does not become unbalanced
    return n;
}
void inorder(struct node *p)
{
    if(p != NULL)
    {
        inorder(p->leftChild);
        cout << p->data << ", ";
        inorder(p->rightChild);
    }
}
void preorder(struct node *p)
{
    if(p != NULL)
    {
        cout << p->data << ", ";
        preorder(p->leftChild);
        preorder(p->rightChild);
    }
}
void print(struct node* root)
{
    cout << "Min Value: " << findMin(root) << endl;
    cout << "Max Value: " << findMax(root) << endl;
    cout << "Pre Order: ";
    preorder(root);
    cout << "Inorder: ";
    inorder(root);
}

int main()
{
    struct node* root = NULL;
    root = insert(root, 10);
    root = insert(root, 20);
    root = insert(root, 30);
    root = insert(root, 40);
    root = insert(root, 50);
    root = insert(root, 25);

//    int array[11] = {25, 5, 6, 17, 34, 2, 57, 89, 12, 12, 73};
//    for(int i = 0; i < 11; i++)
//    {
//        root = insert(root, array[i]);
//    }

    preorder(root);
    return 0;
}

【问题讨论】:

  • 为什么在 C++ 程序中使用malloc?使用new 分配内存。另外,为什么到处都是struct node *?对于 C++,您只需指定 node。似乎您正在阅读 C 材料而不是 C++
  • 顺便说一句,你说的完全正确,我不需要这些。谢谢

标签: c++ insert binary-search-tree


【解决方案1】:

我不确定这是您遇到的唯一问题,但这是一个可能导致严重故障的问题:

struct node* newNode(int element) // helper function to return a new node with empty subtrees
{
    node* newPtr = (struct node*) malloc(sizeof(newPtr));

在这里你分配内存来保存node*

你想要的是保存node的内存。

试试这个:

struct node* newNode(int element) // helper function to return a new node with empty subtrees
{
    node* newPtr = (struct node*) malloc(sizeof(*newPtr));
                                                ^
                                              notice

更新:

在评论中 OP 询问:

这确实解决了它,谢谢!有人可以解释为什么吗?

当你这样做时:

node* newPtr = (struct node*) malloc(sizeof(newPtr));

它是一样的:

node* newPtr = (struct node*) malloc(sizeof(node*));

所以你分配了内存来保存一个指针 - 这可能是 8 或 4 个字节。

然后您开始使用以下代码写入内存区域:

newPtr->data = element;
newPtr->leftChild = NULL;
newPtr->rightChild = NULL;
newPtr->height = 1;

这些写入占用的内存超过了分配的 8(或 4)个字节,因此您写入的内存不属于您的程序。那是未定义的行为(又名“任何事情都可能发生”),从那里分析出了什么问题是没有意义的。

但是,内存很可能在稍后被覆盖(例如在一秒钟后malloc),从而破坏了初始写入的值。所以当你再次读取内存时,你得到的东西与最初写入的值不同,从那里开始,一切都错了。

【讨论】:

  • 由于这个问题被标记为C++,所以malloc的返回值必须被强制转换。
  • 在 C++ 中,您通常应该使用 new node(...) 并避免强制转换。
  • @HansOlsson - 答案的目的是显示“原始代码中的错误” - 这不是为了显示更好的方法。
  • 这确实解决了它,谢谢!有人可以解释为什么吗?
  • @4386427 两者都可以: malloc 可以在 C 和 C++ 中使用,并且需要强制转换并且大小参数是一致的,通常在 C 中没有更好的替代方案。在 C++ 中,有一个替代方案是 new Node(...) ,它避免了强制转换并自动确保一致性(并且必须与“删除”匹配;除非使用智能指针,否则手动匹配)。测试代码清楚地用 C++ 编写。 “授人以鱼,养其一日;授人以渔,养其一生。”
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-08-30
  • 1970-01-01
  • 1970-01-01
  • 2016-01-17
相关资源
最近更新 更多