1

I'm trying to implement a binary search tree in C by using minheap. I've got the basic code down but I cannot get it to work properly. My main problem, according to the build messages, seem to be that I compare pointers and integers. As this code is based off of various explanations that I found in my textbooks and online, I'm not sure how to avoid that.

Here is the complete code (final and working - see edit history for original):

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

typedef struct TreeNode {
    int key;
    struct TreeNode* lChild;
    struct TreeNode* rChild;
} node;

node *createTree(int key) {

    node *root = malloc(sizeof(node));

    root->key = key;
    root->lChild = NULL;
    root->rChild = NULL;

    return(root);

}

void insert(node *root, int key) {

    if (root->key == -1)
        root->key = key;

    else if (key < root->key) {

        if (root->lChild != NULL)
            insert(root->lChild, key);

        else {
            root->lChild = malloc(sizeof(node));
            root->lChild->key = key;
            root->lChild->lChild = NULL;
            root->lChild->rChild = NULL;
        }
    }

    else if (key > root->key) {

        if (root->rChild != NULL)
            insert(root->rChild, key);

        else {
            root->rChild = malloc(sizeof(node));
            root->rChild->key = key;
            root->rChild->lChild = NULL;
            root->rChild->rChild = NULL;
        }
    }

}

void deleteTree(node *root) {

    if (root != NULL) {

        if (root->lChild != NULL)
            deleteTree(root->lChild);

        if (root->rChild != NULL)
            deleteTree(root->rChild);

        free(root);
    }

}

void printNode(node *root) {

    if (root->lChild != NULL) {

        printf("%d -- %d\n", root->key, root->lChild->key);

        if (root->rChild != NULL)
            printf("%d -- %d\n", root->key, root->rChild->key);

        printNode(root->lChild);
    }

    if (root->rChild != NULL) {

        if (root->lChild == NULL)
            printf("%d -- %d\n", root->key, root->rChild->key);

        printNode(root->rChild);
    }
}

void printTree(node *root) {

    printf("graph g {\n");

    printNode(root);

    printf("}\n");
}

void test() {

    // 1.
    node *root1 = createTree(-1);

    insert(root1, 4);
    insert(root1, 2);
    insert(root1, 3);
    insert(root1, 8);
    insert(root1, 6);
    insert(root1, 7);
    insert(root1, 9);
    insert(root1, 12);
    insert(root1, 1);
    printTree(root1);

    // 2.
    node *root2 = createTree(-1);

    insert(root2, 3);
    insert(root2, 8);
    insert(root2, 10);
    insert(root2, 1);
    insert(root2, 7);
    printTree(root2);

    // 3.
    deleteTree(root1);

    // 4.
    deleteTree(root2);

}

int main() {
    test();
}

Here is the output (final and working - see edit history for original):

graph g {
4 -- 2
4 -- 8
2 -- 1
2 -- 3
8 -- 6
8 -- 9
6 -- 7
9 -- 12
}
graph g {
3 -- 1
3 -- 8
8 -- 7
8 -- 10
}

Process returned 1 (0x1)   execution time : 0.712 s
Press any key to continue.

It looks like I need to exclude the connection from "nothing" to the root of the tree still.

printTree I added for debugging purposes but there seem to be problems with it as well.

enenra
  • 111
  • 1
  • 11

2 Answers2

2

First, when you have compiler warnings messages like this, do not try to execute. It means you shall obviously memory leaks somewhere.

And your problem is effectively that you are making a comparaison between an integer and a pointer here :

(root->key == NULL)

According to your structure, root->key is an integer, looks like your node data. But NULL is using to reference as a non-assigned pointer.

You don't have to use your key as a "check" to know if your node is free or not. Every node in a tree has already an assigned value. You should better travel your tree until you reach a NULL node, and then allocate this node from it parent. And that's exactly what you are doing in your heapify function.

In fact, the only thing you should do is to remove your insert function, calling directly heapify instead.

You have another problem here:

printf("key: %d, lChild key: %d, rChild key: %d\n", root->key, root->lChild->key, root->rChild->key);

if (root->lChild != NULL)
[...]

You are printing data in node childs before checking if there is a child at all. That's a big potential source of core dumps.

I recommend to use a recursive algorithm for your printNode as for your other functions :

void printNode(node *root) {
    if (root->lChild != NULL) {
        printf("Left child: ");
        printf("%d -- %d\n", root->key, root->lChild->key);
        printNode(root->rChild);
    }
    if (root->rChild != NULL) {
        printf("Right child: ");
        printf("%d -- %d\n", root->key, root->rChild->key);
        printNode(root->rChild);
    }
}

And for your root key problem, I suggest to make a distinct function to create your tree, taking as parameter your first key :

node *createTree(int rootKey) {
    node *root;

    root = malloc(sizeof(node));
    if (root == NULL) return NULL; // Don't forget to check your malloc's return !
    root->key = rootKey;
    root->lChild = NULL;
    root->rChild = NULL;

    return (root);
}
Aracthor
  • 5,757
  • 6
  • 31
  • 59
  • Thanks! Would it be possible to do this instead of the check for whether root->key is NULL: `if (root == NULL) root->key = key;` ? Or is there another way to check for this without adding another variable to the struct? (the struct was predetermined for this exercise) Regarding the print part - that's actually what I was initially aiming to do but then got confused halfway through and made it a loop... – enenra Apr 12 '15 at 13:20
  • I edited the part on the check on root->key : your `insert` function was not necessary here. Except if your tree have to handle node without data, but if your structure is predetermined, that should not be the case. And for the print part, recursive functions are common in tree management, so you shall have to be used to it, sorry. – Aracthor Apr 12 '15 at 13:46
  • Thanks! I adjusted the code according to your suggestions and will update the OP accordingly in a bit. It still doesn't work quite correctly, though. Recursion for print is no big problem. I just couldn't figure it out at the time and decided to try and do what seemed the easier solution first. I also adjusted the print function you suggested slightly to get an in-order output instead of your pre-order one. I also changed the formatting but that's nothing deal-breaking. – enenra Apr 12 '15 at 13:55
  • I have updated the OP and found another error in my code while doing so. Now the only part left is to exclude the root from having a connection while printing I think. I'll look into how to do that. It also looks like it's missing the 12 in the first tree. – enenra Apr 12 '15 at 14:00
  • You forgot to initialise your root node key. That is why your trees start by a random value. When you create a tree, you usually initialise the root key as your first value to insert rather than inserting it after. Else, your tree seems to be correctly sorted... Try the new version of `printNode`, it should go to any node. Yours don't always go to right children. – Aracthor Apr 12 '15 at 14:06
  • I'm confused. Wasn't initializing the root key what I was doing in my previous `insert()` function which I then replaced with heapify as per your instruction. I'm probably misunderstanding something. How would I make sure that if the root->key is empty that the first value I `insert()` would be made the root->key? Regarding the `print`functions, I actually just found the problem myself as well and will insert the code into OP in a second. – enenra Apr 12 '15 at 14:22
  • Without adding a new attribute in your structure, there is no stable way to check a root node via insert. However, you could make an `createTree(int rootKey)` function that take as parameter only the first key of the tree, and return you a root node with NULL children and a key equals to your parameter. (I added it in my answer) – Aracthor Apr 12 '15 at 14:26
  • 2 things. My exercise actually calls for first creating an empty tree and then adding all values into it. As `createTree()` calls for an initial key value I don't think that would count, sadly. Maybe I could just initialize the tree with root->key = -1 or something like that... Furthermore, am I understanding this correctly that the way to call `createTree()` would be `node *root1 = createTree(value);`? – enenra Apr 12 '15 at 14:44
  • You understood correctly the way to call `createTree()`, and I think it is still a good idea even if you cannot initialise the first key with it. But if you don't have right to give to it an argument, you can still initialise it with a default value - like 0 - to not make it random. Do never let random values somewhere, it's a big leak source. – Aracthor Apr 12 '15 at 14:49
  • Understood. I updated my code accordingly and inserted it into the OP. Thank you for your help! – enenra Apr 12 '15 at 14:56
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/75059/discussion-between-enenra-and-aracthor). – enenra Apr 12 '15 at 15:08
-1

There is a problem in the printTree function:

printf("key: %d, lChild key: %d, rChild key: %d\n", root->key, root->lChild->key, root->rChild->key);

You don't check if root->lChild or root->rChild are NULL. This yields a segmentation fault. If you are using Linux, I would recommend to use valgrind. It will help you debugging C programs, and save you a lot of time.

StefanW
  • 337
  • 2
  • 5
  • Is there something similar for Windows and Code::Blocks? That's what I use. – enenra Apr 12 '15 at 13:21
  • @enenra There is the Windows version of [GDB](http://www.equation.com/servlet/equation.cmd?fa=gdb), if you can export your program as a `.exe` file, you can run it with GDB and try to localise your memory leaks. But it's very less precise than Valgrind. I strongly recommend you to try to work on Linux if you want to progress in C code. – Aracthor Apr 12 '15 at 13:34