C++ 代码中的指针和 AVL 树有问题

Having issues with pointers and AVL tree in C++ code

我刚开始在我的数据结构和算法中学习 AVL 树 class。我在 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 函数,最初,它们将指针作为参数,return 旋转后的指针 ("Node *leftRotate(Node *x)")。我试图通过引用指针参数和 return 类型 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

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

谢谢。

让我们来看看一段代码的原始版本和修改版本,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

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

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

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