【问题标题】:Having issues with pointers and AVL tree in C++ codeC++ 代码中的指针和 AVL 树存在问题
【发布时间】:2021-06-03 18:53:24
【问题描述】:

我刚开始在我的数据结构和算法课程中学习 AVL 树。我在 Geeks for Geeks 上找到了将新节点插入树并平衡它的代码。代码如下所示。

// C++ program to insert a node in AVL tree 
#include <iostream>
using namespace std;
 
// An AVL tree node 
class Node 
{ 
    public:
    int key; 
    Node *left; 
    Node *right; 
    int height; 
}; 
 
// A utility function to get maximum
// of two integers 
int max(int a, int b); 
 
// A utility function to get the 
// height of the tree 
int height(Node *N) 
{ 
    if (N == NULL) 
        return 0; 
    return N->height; 
} 
 
// A utility function to get maximum
// of two integers 
int max(int a, int b) 
{ 
    return (a > b)? a : b; 
} 
 
/* Helper function that allocates a 
   new node with the given key and 
   NULL left and right pointers. */
Node* newNode(int key) 
{ 
    Node* node = new Node();
    node->key = key; 
    node->left = NULL; 
    node->right = NULL; 
    node->height = 1; // new node is initially
                      // added at leaf 
    return(node); 
} 
 
// A utility function to right
// rotate subtree rooted with y 
// See the diagram given above. 
Node *rightRotate(Node *y) 
{ 
    Node *x = y->left; 
    Node *T2 = x->right; 
 
    // Perform rotation 
    x->right = y; 
    y->left = T2; 
 
    // Update heights 
    y->height = max(height(y->left),
                    height(y->right)) + 1; 
    x->height = max(height(x->left),
                    height(x->right)) + 1; 
 
    // Return new root 
    return x; 
} 
 
// A utility function to left 
// rotate subtree rooted with x 
// See the diagram given above. 
Node *leftRotate(Node *x) 
{ 
    Node *y = x->right; 
    Node *T2 = y->left; 
 
    // Perform rotation 
    y->left = x; 
    x->right = T2; 
 
    // Update heights 
    x->height = max(height(x->left),    
                    height(x->right)) + 1; 
    y->height = max(height(y->left), 
                    height(y->right)) + 1; 
 
    // Return new root 
    return y; 
} 
 
// Get Balance factor of node N 
int getBalance(Node *N) 
{ 
    if (N == NULL) 
        return 0; 
    return height(N->left) - height(N->right); 
} 
 
// Recursive function to insert a key
// in the subtree rooted with node and
// returns the new root of the subtree. 
Node* insert(Node* node, int key) 
{ 
    /* 1. Perform the normal BST insertion */
    if (node == NULL) 
        return(newNode(key)); 
 
    if (key < node->key) 
        node->left = insert(node->left, key); 
    else if (key > node->key) 
        node->right = insert(node->right, key); 
    else // Equal keys are not allowed in BST 
        return node; 
 
    /* 2. Update height of this ancestor node */
    node->height = 1 + max(height(node->left), 
                        height(node->right)); 
 
    /* 3. Get the balance factor of this ancestor 
        node to check whether this node became 
        unbalanced */
    int balance = getBalance(node); 
 
    // If this node becomes unbalanced, then 
    // there are 4 cases 
 
    // Left Left Case 
    if (balance > 1 && key < node->left->key) 
        return rightRotate(node); 
 
    // Right Right Case 
    if (balance < -1 && key > node->right->key) 
        return leftRotate(node); 
 
    // Left Right Case 
    if (balance > 1 && key > node->left->key) 
    { 
        node->left = leftRotate(node->left); 
        return rightRotate(node); 
    } 
 
    // Right Left Case 
    if (balance < -1 && key < node->right->key) 
    { 
        node->right = rightRotate(node->right); 
        return leftRotate(node); 
    } 
 
    /* return the (unchanged) node pointer */
    return node; 
} 
 
// A utility function to print preorder 
// traversal of the tree. 
// The function also prints height 
// of every node 
void preOrder(Node *root) 
{ 
    if(root != NULL) 
    { 
        cout << root->key << " "; 
        preOrder(root->left); 
        preOrder(root->right); 
    } 
} 
 
// Driver Code
int main() 
{ 
    Node *root = NULL; 
     
    /* Constructing tree given in 
    the above figure */
    root = insert(root, 10); 
    root = insert(root, 20); 
    root = insert(root, 30); 
    root = insert(root, 40); 
    root = insert(root, 50); 
    root = insert(root, 25); 
     
    /* The constructed AVL Tree would be 
                30 
            / \ 
            20 40 
            / \ \ 
        10 25 50 
    */
    cout << "\n\n" << "Preorder traversal of the "
            "constructed AVL tree is \n"; 
    preOrder(root); 
    cout << "\n\n";
     
    return 0; 
} 
 
// This code is contributed by
// rathbhupendra

我理解代码的工作原理非常好(至少我认为我理解)。我稍微编辑了代码以尝试稍微不同的方法。对于leftRotate和rightRotate函数,最初它们以指针为参数,返回旋转后的指针(“Node *leftRotate(Node *x)”)。我试图通过引用指针参数传递并返回类型 void ("void leftRotate(Node*& x)") 来改变这一点。代码如下所示(我在更改原始代码的所有位置旁边添加了注释“//CHANGED”)。

// C++ program to insert a node in AVL tree 
#include <iostream>
using namespace std;
 
// An AVL tree node 
class Node 
{ 
    public:
    int key; 
    Node *left; 
    Node *right; 
    int height; 
}; 
 
// A utility function to get maximum
// of two integers 
int max(int a, int b); 
 
// A utility function to get the 
// height of the tree 
int height(Node *N) 
{ 
    if (N == NULL) 
        return 0; 
    return N->height; 
} 
 
// A utility function to get maximum
// of two integers 
int max(int a, int b) 
{ 
    return (a > b)? a : b; 
} 
 
/* Helper function that allocates a 
   new node with the given key and 
   NULL left and right pointers. */
Node* newNode(int key) 
{ 
    Node* node = new Node();
    node->key = key; 
    node->left = NULL; 
    node->right = NULL; 
    node->height = 1; // new node is initially
                      // added at leaf 
    return(node); 
} 
 
// A utility function to right
// rotate subtree rooted with y 
// See the diagram given above. 
void rightRotate(Node*& y) //CHANGED
{ 
    Node *x = y->left; 
    Node *T2 = x->right; 
 
    // Perform rotation 
    x->right = y; 
    y->left = T2; 
 
    // Update heights 
    y->height = max(height(y->left),
                    height(y->right)) + 1; 
    x->height = max(height(x->left),
                    height(x->right)) + 1; 
 
    // Return new root 
    //return x; //CHANGED
} 
 
// A utility function to left 
// rotate subtree rooted with x 
// See the diagram given above. 
void leftRotate(Node*& x) //CHANGED
{ 
    Node *y = x->right; 
    Node *T2 = y->left; 
 
    // Perform rotation 
    y->left = x; 
    x->right = T2; 
 
    // Update heights 
    x->height = max(height(x->left),    
                    height(x->right)) + 1; 
    y->height = max(height(y->left), 
                    height(y->right)) + 1; 
 
    // Return new root 
    //return y; //CHANGED
} 
 
// Get Balance factor of node N 
int getBalance(Node *N) 
{ 
    if (N == NULL) 
        return 0; 
    return height(N->left) - height(N->right); 
} 
 
// Recursive function to insert a key
// in the subtree rooted with node and
// returns the new root of the subtree. 
Node* insert(Node* node, int key) 
{ 
    /* 1. Perform the normal BST insertion */
    if (node == NULL) 
        return(newNode(key)); 
 
    if (key < node->key) 
        node->left = insert(node->left, key); 
    else if (key > node->key) 
        node->right = insert(node->right, key); 
    else // Equal keys are not allowed in BST 
        return node; 
 
    /* 2. Update height of this ancestor node */
    node->height = 1 + max(height(node->left), 
                        height(node->right)); 
 
    /* 3. Get the balance factor of this ancestor 
        node to check whether this node became 
        unbalanced */
    int balance = getBalance(node); 
 
    // If this node becomes unbalanced, then 
    // there are 4 cases 
 
    // Left Left Case 
    if (balance > 1 && key < node->left->key) 
        rightRotate(node); //CHANGED
 
    // Right Right Case 
    if (balance < -1 && key > node->right->key) 
        leftRotate(node); //CHANGED
 
    // Left Right Case 
    if (balance > 1 && key > node->left->key) 
    { 
        leftRotate(node->left); //CHANGED
        rightRotate(node); //CHANGED
    } 
 
    // Right Left Case 
    if (balance < -1 && key < node->right->key) 
    { 
        rightRotate(node->right); //CHANGED
        leftRotate(node); //CHANGED
    } 
 
    /* return the (unchanged) node pointer */
    return node; 
} 
 
// A utility function to print preorder 
// traversal of the tree. 
// The function also prints height 
// of every node 
void preOrder(Node *root) 
{ 
    if(root != NULL) 
    { 
        cout << root->key << " "; 
        preOrder(root->left); 
        preOrder(root->right); 
    } 
} 
 
// Driver Code
int main() 
{ 
    Node *root = NULL; 
     
    /* Constructing tree given in 
    the above figure */
    root = insert(root, 10); 
    root = insert(root, 20); 
    root = insert(root, 30); 
    root = insert(root, 40); 
    root = insert(root, 50); 
    root = insert(root, 25); 
     
    /* The constructed AVL Tree would be 
                30 
            / \ 
            20 40 
            / \ \ 
        10 25 50 
    */
    cout << "\n\n" << "Preorder traversal of the "
            "constructed AVL tree is \n"; 
    preOrder(root); 
    cout << "\n\n";
     
    return 0; 
} 
 
// This code is contributed by
// rathbhupendra

我这样做的逻辑是,我可以通过引用传递指针,而不是返回旋转的指针,从而允许函数对指针本身进行旋转,而不是指针的副本。但是,当我运行此代码时,我得到分段错误或没有输出(在 GeeksforGeeks 编码环境中它的分段错误和在 VS 代码上它只是没有输出)。有什么我也应该改变的,还是我做错了。我之前在之前的代码中已经像这样通过引用传递了指针,它们都按预期工作,所以我不明白为什么这不起作用。任何理解这一点的帮助将不胜感激。

谢谢。

【问题讨论】:

    标签: c++ pointers pass-by-reference avl-tree


    【解决方案1】:

    我们来看看一段sn-p代码的原版和修改版,explain everything that's happening here to your rubber duck

    // Right Right Case 
    if (balance < -1 && key > node->right->key) 
        return leftRotate(node); 
    

    之前,leftRotate 被调用来旋转node,并且旋转后的node从这个函数中返回node 是这个函数的一个参数)。这就是你将如何描述你的橡皮鸭在这里发生的事情。

    现在,让我们试着向你的橡皮鸭解释这段代码的修改版本:

    // Right Right Case 
    if (balance < -1 && key > node->right->key) 
        leftRotate(node); //CHANGED
    

    你看,橡皮鸭先生,leftRotate 被调用了,它用leftRotate() 的原始版本返回的内容修改了通过引用传递的参数,就是这样!

    你的橡皮鸭现在会指出你的问题并说:“好吧!但是你现在应该注意到一个明显的区别:leftRotate() 最初返回的值,现在是新的 @987654331 @,不再从这个函数中得到returned。代码的“CHANGED”版本在逻辑上不等同于原始版本!”

    看看你的橡皮鸭有多大帮助?同样的逻辑错误在更改后的版本中多次出现,引用更改后的leftRotaterightRotate

    【讨论】:

    • 我觉得我不太明白。它们在逻辑上如何不等效。在这些函数中,没有分配新内存,因此任何声明和分配的指针都指向相同的地址。所以,“节点 *x = y->left;”例如,这使得 x 指向与“y->left”相同的地址,所以通过更改“x->right”,我实际上是在更改“y->left->right”,不是吗?跨度>
    • 这与任何指向其他任何事物的事物无关。在原始版本中,您 return 来自函数。在修改后的版本中,您不要。尝试将returns 添加到各种功能中,在随机位置(或删除一些现有的),看看事情是否继续像以前一样工作。他们不会的。
    • 哦,好吧,我想我明白你的意思了。在我的版本中,我只是更改了y 指针(或x),但仍需要返回新的根。我将其改回以返回新节点指针,但如果我将其保留为通过引用参数传递,我将无法获得正确的输出(我将其从 void rightRotate(Node*&amp; y) 更改为 Node* rightRotate(Node*&amp; y) )。输出应该是:“构造的 AVL 树的预序遍历是 30 20 10 25 40 50”。相反,我得到:“构造的 AVL 树的预序遍历是 40 30 25 50”你知道这是为什么吗?
    • 这一定是由于两个版本之间的另一个逻辑差异。你只需要找到它们。由于您已经对显示的代码进行了更多更改,因此任何人都不可能就某些甚至没有显示的不同版本的代码发表任何声明。也许您犯了某种错字或错误,进行了额外的修复,这无法确定。
    • 嗯,好的。好吧,谢谢你的帮助。我会用新代码发布另一个问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多