簡體   English   中英

AVL樹平衡-C

[英]AVL Tree Balancing - C

我一直在嘗試用C語言編寫一個簡單的AVL樹實現。它也支持重復值。 一切似乎都正常,但有時我會得到一棵糟糕的平衡樹。 在我看來,旋轉功能似乎可以正常工作。 我在想高度檢查有問題,但我似乎找不到問題。

我僅從插入中得到的樹是不平衡的,因此插入是有問題的。 然后,在此之前,在刪除之后,樹的平衡通常很差。 但是有時它是適當平衡的,我似乎無法確定如何做到。

此實現的代碼如下:

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

#define SPACE_PER_NODE 2
#define MAX(x, y) (x) > (y) ? (x) : (y)


enum delete_flags {
    DELETE_NO_FORCE,
    DELETE_FORCE
};

typedef unsigned int uint;

struct tree_elem {
    int data;
    uint dup_count;
    int height;
    struct tree_elem* left;
    struct tree_elem* right;
};

typedef struct tree_elem node;

node* create_bst();
void insert(node**, int);
void delete_elem(node**, int, uint);
node* search(node*, int);
node* get_parent(node*, node*);
node* find_min(node*);
node* get_successor(node*, node*);
uint max_depth(node*);
void display_helper(node*, int);
void display_tree(node*);
int get_height(node*);
void rotate_once_left(node**);
void rotate_once_right(node**);
void rotate_twice_left(node**);
void rotate_twice_right(node**);

void* s_malloc (const uint t) {
    void* p = malloc(t);
    if(!p) {
        printf("Out of memory.\n");
        exit(EXIT_FAILURE);
    }
    return p;
}

void s_free (void* p) {
    if(!p) {
        printf("Error: Tried to free NULL ptr.\n");
        exit(EXIT_FAILURE);     
    }
    else
        free(p);
}

node* create_bst(int data) {
    node* tree = (node*) s_malloc(sizeof(node));
    tree->left = tree->right = NULL;
    tree->data = data;
    return tree;
}

void insert(node** t, int val) {
    if(!(*t)) {
        *t = (node*) s_malloc(sizeof(node));
        (*t)->data = val;
        (*t)->left = (*t)->right = NULL;
        (*t)->dup_count = 0;
        (*t)->height = 0;
        return;
    }
    if((*t)->data < val) {
        insert(&(*t)->right, val);

        if(get_height((*t)->right) - get_height((*t)->left) >= 2) {
            if((*t)->right->data < val)
                rotate_once_right(&(*t));
            else if((*t)->right->data > val)
                rotate_twice_right(&(*t));
        }
    }
    else if((*t)->data > val) {
        insert(&(*t)->left, val);

        if(get_height((*t)->left) - get_height((*t)->right) >= 2) {
            if((*t)->left->data > val)
                rotate_once_left(&(*t));
            else if((*t)->left->data < val)
                rotate_twice_left(&(*t));
        }
    }
    else {
        ++(*t)->dup_count;
        return;                                                             // this is important! if there are duplicates, they might cause an unwanted height change!
    }
    (*t)->height = MAX(get_height((*t)->left), get_height((*t)->right)) + 1;
}

node* get_successor(node* t, node* s) {
    if(s->right)
        return find_min(s->right);
    node* suc = NULL;
    node* temp = t;
    // Start from root and search for successor in the tree
    while (temp) {
        if (s->data < temp->data) {
            suc = temp;
            temp = temp->left;
        }
        else if (s->data > temp->data)
            temp = temp->right;
        else
           break;
    }
    return suc;
}

void free_tree (node* t) {
    if (!t)
        return;
    free_tree(t->left);
    free_tree(t->right);
    free(t);
}

node* search(node* t, int val) {
    if(!t)
        return NULL;
    if(t->data == val)
        return t;
    else if(t->data < val)
        return search(t->right, val);
    return search(t->left, val);
}

node* find_min(node* t) {
    node* temp = t;
    while(temp->left)
        temp = temp->left;
    return temp;
}

uint max_depth(node* t) {
   if (!t)
       return 0;
   int ldepth = max_depth(t->left);
   int rdepth = max_depth(t->right);
   if (ldepth > rdepth)
       return ldepth + 1;
   return rdepth + 1;
}

void display_helper(node* t, int spaces) {
    int width = ceil(log10(max_depth(t)+0.01)) + 2;
    wchar_t* sp64 = L"                                                                ";
    if (!t) {
        wprintf(L"\n");
        return;
    }
    display_helper(t->right, spaces + width);
    wprintf(L"%*.*s%d\n", 0, spaces, sp64, t->data);
    display_helper(t->left, spaces + width);
}

void display_tree(node* t) {
    if(t)
        display_helper(t, SPACE_PER_NODE);
}

int get_height(node* t) {
    if(!t)
        return 0;
    return t->height;
}

void rotate_once_left(node** k1) {
    node* temp = (*k1)->left;
    (*k1)->left = temp->right;
    temp->right = *k1;

    (*k1)->height = MAX(get_height((*k1)->left), get_height((*k1)->right)) + 1;
    temp->height = MAX(get_height(temp->left), (*k1)->height) + 1;

    *k1 = temp;
}


void rotate_once_right(node** k1) {
    node* temp = (*k1)->right;
    (*k1)->right = temp->left;
    temp->left = *k1;

    (*k1)->height = MAX(get_height((*k1)->left), get_height((*k1)->right)) + 1;
    temp->height = MAX(get_height(temp->right), (*k1)->height) + 1;

    *k1 = temp;
}

void rotate_twice_left(node** k1) {
    rotate_once_right(&(*k1)->left);
    rotate_once_left(k1);
}

void rotate_twice_right(node** k1) {
    rotate_once_left(&(*k1)->right);
    rotate_once_right(k1);
}

int main() {
    srand(time(NULL));
    node* tree = create_bst(rand() % 15 + 1);
    for(uint i = 0; i < 14; ++i) {
        int elem;
        // create unique elements from 1 to 20.
        do {
            elem = rand() % 15 + 1;
        } while (search(tree, elem));
        insert(&tree, elem);
    }
    display_tree(tree);
    int input;
    do {
        printf("Enter value to delete: ");
        scanf("%d", &input);
        delete_elem(&tree, input, DELETE_NO_FORCE);
        display_tree(tree);
    } while(input != -1);
    return 0;
}

一個值得一看的地方就是您的MAX宏。

MAX(get_height((*t)->left), get_height((*t)->right)) + 1;

可能無法計算出您認為的結果。

在當今時代,當編譯器以如此出色的性能內聯時,您不應使用宏進行此計算。 這不僅不正確,而且幾乎可以肯定效率較低。

在這里,我將與我在評論中所說的保持一致:您應該強烈考慮測試驅動的開發。 編寫一個謂詞,檢查給定樹是否滿足AVL條件,包括它是否是有效的BST。 現在,將項目添加到一棵空樹中,並在每一個之后運行謂詞。 當它報告樹不是AVL時,您將能夠查看出了什么問題。 否則,您將更有信心代碼可以按預期工作。

編輯

好的,手動擴展宏(添加一些空格):

(get_height((*t)->left)) > (get_height((*t)->right)) 
    ? (get_height((*t)->left))
    : (get_height((*t)->right)) + 1;

+ 1僅影響else分支。 您需要另外一組括號才能得到正確的答案。

此外,高度被計算兩次。 使用一個功能,它只會發生一次。 公認的是,積極的優化器也可能會消除重復的計算,但是這種優化比僅內聯max()函數要復雜得多,因此比較脆弱。 為什么要使用宏來使編譯器的工作更困難

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM