C语言 AVL树删除导致错误遍历

xxls0lw8  于 2023-06-05  发布在  其他
关注(0)|答案(1)|浏览(141)

编制了C程序菜单,实现了AVL树数据的插入、删除和打印。但删除后有些不对劲。
已输入的AVL树横向打印的结果是正确的,但是当删除它并再次打印它时,它看起来像是有问题。
这里我输入的是::6,27,19,11,36,14,81,63,75这里输出横截:前序遍历:19 11 6 14 36 27 75 63 81中序遍历:6 11 14 19 27 36 63 75 81后序遍历:6 14 11 27 63 81 75 36 19
我相信这是正确的。
但删除后:14、75、36、19、11前序遍历:27 6 63 81中序遍历:6 27 63 81后序遍历:六八一六三二七
我认为这是不正确的,因为它应该是:前序遍历:63 6 27 81中序遍历:27 6 63 81后序遍历:27 6 81 63
下面是我的代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct Node{
    int data;
    struct Node* left;
    struct Node* right;
    int height;
};

int height(struct Node* node){
    if (node == NULL)
        return 0;
    return node->height;
}

int max(int a, int b){
    return (a>b)? a : b;
}

struct Node* newNode(int data){
    struct Node* node = (struct Node*)malloc(sizeof(struct Node));
    node->data = data;
    node->left = NULL;
    node->right = NULL;
    node->height = 1;
    return node;
}

struct Node* rightRotate(struct Node* y){
    struct Node* x = y->left;
    struct Node* T2 = x->right;

    x->right = y;
    y->left = T2;

    y->height = max(height(y->left), height(y->right)) + 1;
    x->height = max(height(x->left), height(x->right)) + 1;

    return x;
}

struct Node* leftRotate(struct Node* x){
    struct Node* y = x->right;
    struct Node* T2 = y->left;

    y->left = x;
    x->right = T2;

    x->height = max(height(x->left), height(x->right)) + 1;
    y->height = max(height(y->left), height(y->right)) + 1;

    return y;
}

int getBalance(struct Node* node){
    if (node == NULL)
        return 0;
    return height(node->left) - height(node->right);
}

struct Node* insert(struct Node* node, int data){
    if(node == NULL)
        return newNode(data);

    if(data < node->data)
        node->left = insert(node->left, data);
    else if(data > node->data)
        node->right = insert(node->right, data);
    else 
        return node;

    node->height = 1 + max(height(node->left), height(node->right));

    int balance = getBalance(node);

    if(balance > 1 && data < node->left->data)
        return rightRotate(node);

    if(balance < -1 && data > node->right->data)
        return leftRotate(node);

    if(balance > 1 && data > node->left->data) {
        node->left = leftRotate(node->left);
        return rightRotate(node);
    }

    if(balance < -1 && data < node->right->data) {
        node->right = rightRotate(node->right);
        return leftRotate(node);
    }

    return node;
}

struct Node* minValueNode(struct Node* node){
    struct Node* current = node;

    while (current->left != NULL)
        current = current->left;

    return current;
}

struct Node* deleteNode(struct Node* root, int data){
    if(root == NULL)
        return root;

    if(data < root->data)
        root->left = deleteNode(root->left, data);

    else if(data > root->data)
        root->right = deleteNode(root->right, data);

    else{
        if((root->left == NULL) || (root->right == NULL)) {
            struct Node* temp = root->left ? root->left : root->right;

            if(temp == NULL){
                temp = root;
                root = NULL;
            } else 
                *root = *temp; 

            free(temp);
        } else{

            struct Node* temp = minValueNode(root->right);

            root->data = temp->data;

            root->right = deleteNode(root->right, temp->data);
            
        }
    }

    if(root == NULL)
        return root;

    root->height = 1 + max(height(root->left), height(root->right));

    int balance = getBalance(root);

    if(balance > 1 && getBalance(root->left) >= 0)
        return rightRotate(root);

    if(balance > 1 && getBalance(root->left) < 0) {
        root->left = leftRotate(root->left);
        return rightRotate(root);
    }

    if(balance < -1 && getBalance(root->right) >= 0) {
        root->right = rightRotate(root->right);
        return leftRotate(root);
    }

    if(balance < -1 && getBalance(root->right) < 0)
        return leftRotate(root);

    return root;
}

struct Node* search(struct Node* root, int data) {
    if(root == NULL || root->data == data)
        return root;

    if(root->data < data)
        return search(root->right, data);

    return search(root->left, data);
}

void preorder(struct Node* root) {
    if(root != NULL) {
        printf("%d ", root->data);
        preorder(root->left);
        preorder(root->right);
    }
}

void inorder(struct Node* root) {
    if(root != NULL) {
        inorder(root->left);
        printf("%d ", root->data);
        inorder(root->right);
    }
}

void postorder(struct Node* root) {
    if(root != NULL) {
        postorder(root->left);
        postorder(root->right);
        printf("%d ", root->data);
    }
}

int main(){
    struct Node* root = NULL;
    int choice, data;

    do{
        printf("\n1. Insertion");
        printf("\n2. Deletion");
        printf("\n3. Traversal");
        printf("\n4. Exit");
        printf("\nChoose: ");
        scanf("%d", &choice);

        switch(choice){
            case 1:
                printf("Enter the value to be inserted: ");
                scanf("%d", &data);
                root = insert(root, data);
                break;

            case 2:
                printf("Enter the value to be deleted: ");
                scanf("%d", &data);
                if (search(root, data) == NULL) {
                    printf("Data not found.\n");
                } else {
                    root = deleteNode(root, data);
                    printf("Data deleted successfully.\n");
                }
                break;

            case 3:
                printf("\nPreorder traversal: ");
                preorder(root);
                printf("\nInorder traversal: ");
                inorder(root);
                printf("\nPostorder traversal: ");
                postorder(root);
                printf("\n");
                break;

            case 4:
                printf("Thank You\n");
                break;

            default:
                printf("Invalid choice\n");
                break;
        }
    } while(choice != 4);

    return 0;
}
oknwwptz

oknwwptz1#

已输入的AVL树横向打印的结果是正确的,但是当删除它并再次打印它时,它看起来像是有问题。

你错了

这里我输入的是::6,27,19,11,36,14,81,63,75这里输出横截:前序遍历:19 11 6 14 36 27 75 63 81中序遍历:6 11 14 19 27 36 63 75 81后序遍历:6 14 11 27 63 81 75 36 19
我相信这是正确的。
按序遍历反映正确的BST结构(它以数字顺序遍历节点)。从前序或后序遍历中,我们可以重建实际的树:

19
    /    \
  11      36
 /  \    /  \
6  14  27   75
           /  \
          63   81

这是一棵有效的AVL树:没有节点具有高度相差超过一的子树。
但删除后:14、75、36、19、11前序遍历:27 6 63 81中序遍历:6 27 63 81后序遍历:六八一六三二七
我认为这是不正确的,因为它应该是:前序遍历:63 6 27 81中序遍历:27 6 63 81后序遍历:27 6 81 63
如果删除后的树具有您预测的有序遍历,那么它将不是有效的BST,更不用说AVL树了。但是,您提供的顺序也与您提供的前顺序和后顺序不一致。

**观察结果是否正确?**它反映了最后一棵树:

27
 /  \
6    63
      \
       81

这是一个包含预期元素的有效AVL树,因此它是正确的结果是合理的。
然而,还有另一个有效的AVL树包含相同的元素(由你的前序和后序预测给出的,其根为63),所以让我们仔细看看实际的删除:

  • 14是一个叶节点,它的兄弟节点也是一个叶节点,所以删除14不需要任何额外的重组。
  • 75的两个子节点都是叶节点。当删除75时,其中一个需要提升为另一个的父级以保持BST条件,但这不会使树不平衡。一个有效的结果是:
19
    /    \
  11      36
 /       /  \
6      27    63
              \
               81
  • 当删除36时,需要选择左子树中的最右节点(27)或右子树中的最左节点(63)来替换它。如果我们选择27,那么结果将是不平衡的,我们需要执行旋转来重新平衡它。如果我们选择63,那么结果将是平衡的。事实证明,无论哪种方式,最终结果都是一样的:
19
    /    \
  11      63
 /       /  \
6      27    81
  • 注意:在上一步骤中,在提升63或81之间的选择在这里被讨论。任何一种选择都会导致当前步骤中的相同结果树。*
  • 现在我们删除19。同样,我们选择左子树的最右节点(11)或右子树的最左节点(27)作为其替换。不管怎样,树是平衡的。可能的结果是:
11                   27
    /    \               /    \
   6      63     OR    11      63
         /  \         /          \
       27    81      6            81

      A                    B

1.最后,我们删除11。
关于备选方案(B)的结果是容易的:向上移动6次,树仍然平衡,完成。最终结果:

27
   /    \
  6      63
           \
            81
     B

注意这是实际观察到的最终树。
关于(A)的结果有两种选择:
1.我们从右子树(27)中选择替换。在这种情况下不需要重新平衡:

27
   /    \
  6      63
           \
            81
     A1

注意这也是观察到的最终树。为了完整,然而…
1.我们从左子树(6)中选择替换。在这种情况下,我们需要重新平衡,应用旋转来转换:

6
         \
          63
         /  \
       27    81

      A2'

...最终的结果是:

63
    /    \
   6      81
    \
     27

      A2

这是我上面提到的另一种AVL树,也是你预测的。
最终,观察到的结果对于正常运行的AVL树例程是合理的,尽管你的期望(根据前序和后序判断)也是合理的。这个特殊的测试并没有完全执行维护AVL树所需的行为,但是AVL实现似乎确实通过了它,除非您必须遵守附加的约束,而不仅仅是在每个操作中维护AVL条件。
然而,作为一种实现策略,当删除子树高度不同的节点时,从较高的子树中选择替换是有意义的。这将倾向于减少需要执行的再平衡的量。对于该测试用例,使用该策略的AVL例程将仅生成根为27的结果。

相关问题