При вставке (ключ, значение) в дерево, узел не формирует древовидную структуру - PullRequest
0 голосов
/ 27 марта 2019

Я пытаюсь создать дерево avl, которое читает (ключ, значение) пару по одному из файла и формирует дерево на основе данных ключа.

Сначала, чтобы прочитать кортежи в ключи значение и передал их в функцию создания, где я инициализировал дерево со структурой

typedef struct AVLTree{
    int  size;      // count of items in avl tree
    AVLTreeNode *root; // root
} AVLTree;

AVLTree *newAVLTree()
{
    AVLTree *T;
    T = malloc(sizeof (AVLTree));
    assert (T != NULL);
    T->size = 0;
    T->root = NULL;
    return T;
}

, затем я присваиваю значение корня дерева, которое изначально равно NULL, AVLTreeNode, структура которого выглядитнапример:

typedef struct AVLTreeNode {
    int key; //key of this item
    int  value;  //value (int) of this item
    int height; //height of the subtree rooted at this node
    struct AVLTreeNode *parent; //pointer to parent
    struct AVLTreeNode *left; //pointer to left child
    struct AVLTreeNode *right; //pointer to right child
} AVLTreeNode;

//data type for AVL trees
typedef struct AVLTree{
    int  size;      // count of items in avl tree
    AVLTreeNode *root; // root
} AVLTree;

// create a new AVLTreeNode
AVLTreeNode *newAVLTreeNode(int k, int v )
{
    AVLTreeNode *new;
    new = malloc(sizeof(AVLTreeNode));
    assert(new != NULL);
    new->key = k;
    new->value = v;
    new->height = 0; // height of this new node is set to 0
    new->left = NULL; // this node has no child
    new->right = NULL;
    new->parent = NULL; // no parent
    return new;
}

Теперь для каждого ключа, пары значений, которую я прочитал из файла, я передаю ее в функцию создания и проверяю 3 условия следующим образом:

void insert_in_tree(int key, int value, struct AVLTreeNode **node){


    // if the tree is empty
    if(*node == NULL){
        node = newNode;
    }
    // insert on left if the data in the key is less than the data in the node.
    else if (key<(*node)->key){
        insert_in_tree(key,value,&(*node)->left);
    }
    // insert on right if the data in the key is greater than the data in the node.
    else if(key>(*node)->key)
    {
        insert_in_tree(key,value,&(*node)->right);
    }

}

PS: Не беспокойтесь о части 'value' в новом AVLTreeNode, так как позже я буду обрабатывать дубликаты, используя это.

С помощью приведенного выше кода я ожидал, что дерево будет сформировано, но этого не произошло.После дальнейшего изучения и отладки я обнаружил, что хотя insert_in_tree передается с новым ключом и значением, узел также является новым вместо уже созданного.

AVLTree *CreateAVLTree(const char *filename)
{
    //Inititalising a new tree
    AVLTree *tree = newAVLTree();
// initialising the head to root of tree i.e. null
    AVLTreeNode *head = tree->root;
    int key, value;
    FILE* file = fopen(filename, "r"); // open a file
    if(file == NULL) {
        return 1;                                   // error checking
    }
    while (fscanf (file, " (%d,%d)", &key, &value) == 2)  // check for number of conversions
    //  space added  here ^
    {
        insert_in_tree(key, value, &head);
        //printf("%d,%d\n", key, value);
    }
    fclose(file);

    return tree;
}
int main() //sample main for testing
{
    AVLTree *tree1;
    tree1=CreateAVLTree("File1.txt");
    //PrintAVLTree(tree1);
    return 0;
}

Я пытался быть настолько сложным, каквозможно, но не стесняйтесь задавать мне вопросы, если вы не понимаете.Рад ответить.Пожалуйста, помогите.Nodes are formed, but the tree is still NULL

Ответы [ 3 ]

2 голосов
/ 27 марта 2019

Михаил был почти там, но не заметил утечки памяти.Я исправляю это ниже:

void insert_in_tree(int key, int value, struct AVLTreeNode **node){
    // if the tree is empty
    if(*node == NULL){
        *node = newAVLTreeNode(key, value);
    }
    // insert on left if the data in the key is less than the data in the node.
    else if (key<(*node)->key){
        insert_in_tree(key,value,&(*node)->left);
    }
    // insert on right if the data in the key is greater than the data in the node.
    else if(key>(*node)->key)
    {
        insert_in_tree(key,value,&(*node)->right);
    }
}

В качестве краткого изложения исправлений:

  • вам нужно было передать указатель на место, где нужно разместить вновь выделенный узел, потому что Cпроходит по значению.Это называется «двойным указателем».

  • вы выделяете память на каждый рекурсивный вызов, но вам это нужно только после того, как вы знаете, куда вставить.

1 голос
/ 27 марта 2019

В функции insert_in_tree вы пытаетесь изменить параметр, который был передан по значению.Вам нужно передать это по ссылке так:

void insert_in_tree(int key, int value, struct AVLTreeNode **node){
    // if the tree is empty
    if(*node == NULL){
        *node = newAVLTreeNode(key, value);
    }
    // insert on left if the data in the key is less than the data in the node.
    else if (key<(*node)->key){
        insert_in_tree(key,value,&(*node)->left);
    }
    // insert on right if the data in the key is greater than the data in the node.
    else if(key>(*node)->key)
    {
        insert_in_tree(key,value,&(*node)->right);
    }
}

Также, в случае node != NULL эта функция приводит к утечке памяти, поскольку она выделяет новый узел, но нигде не сохраняет указатель на него.

Кстати, вы пытаетесь создать не дерево AVL, а дерево двоичного поиска .

0 голосов
/ 27 марта 2019

больше исправлений, уже описанных в ответах:

void insert_in_tree(int key, int value, struct AVLTreeNode **node){
    // if the tree is empty
    if(*node == NULL){
        *node = newAVLTreeNode(key, value);
    }
    // insert on left if the data in the key is less than the data in the node.
    else if (key<(*node)->key){
        insert_in_tree(key,value,&(*node)->left);
    }
    // insert on right if the data in the key is greater than the data in the node.
    else if(key>(*node)->key)
    {
        insert_in_tree(key,value,&(*node)->right);
    }
}

в CreateAVLTree вы (сейчас) задаете дерево узла в голове но вы пропустили обновление tree->root, проще всего не использовать временную переменную head , а использовать tree->root напрямую:

AVLTree *CreateAVLTree(const char *filename)
{
    //Inititalising a new tree
    AVLTree *tree = newAVLTree();
    // initialising the head to root of tree i.e. null
    int key, value;
    FILE* file = fopen(filename, "r"); // open a file
    if(file == NULL) {
        return NULL;                                   // error checking
    }
    while (fscanf (file, " (%d,%d)", &key, &value) == 2)  // check for number of conversions
    //  space added  here ^
    {
        insert_in_tree(key, value, &tree->root);
        //printf("%d,%d\n", key, value);
    }

    fclose(file);

    return tree;
}

Я также заменил недействительныйreturn 1; на return NULL;, когда файл не может быть открыт.

Обратите внимание, что поле size не установлено, возможно, оно должно содержать размер списка узлов, в этом случае просто добавьтеtree->size += 1; рядом с вызовом insert_in_tree

Если я добавлю определения, чтобы напечатать результат:

void PrintNodes(struct AVLTreeNode * l)
{
  if (l == NULL)
    printf("()");
  else {
    putchar('(');
    PrintNodes(l->left);
    printf(" %d %d %d ", l->key, l->value, l->height);
    PrintNodes(l->right);
    putchar(')');
  }
}

void PrintAVLTree(AVLTree * tree)
{
  printf("%d elements : ", tree->size);
  PrintNodes(tree->root);
  putchar('\n');
}

и выведу PrintAVLTree(tree1); из комментария в main , компиляция и исполнение:

pi@raspberrypi:/tmp $ gcc -g -pedantic -Wextra -Wall t.c
pi@raspberrypi:/tmp $ cat File1.txt 
(2, 50) (4, 30) (9, 30) (10, 400) (-5, -40)
(7, 20) (19, 200) (20, 50) (-18, -200) (-2, 29)
(2, 67) (4, 35) (9, 45) (-18, 100)
pi@raspberrypi:/tmp $ ./a.out
14 elements : (((() -18 -200 0 ()) -5 -40 0 (() -2 29 0 ())) 2 50 0 (() 4 30 0 ((() 7 20 0 ()) 9 30 0 (() 10 400 0 (() 19 200 0 (() 20 50 0 ()))))))
...