3

A painfully stupid question that I am almost too ashamed to ask. I have been searching for the past 4 hours, tested different algorithms, tried quite a few on paper and still can't get it to work.

I will spare you the details of the project implementation, but the basic question is: "How do you handle inserting nodes in a Pre-Order Binary Tree.

By Pre-Order BST, I mean that all nodes should be inserted in such a way, that traversing the tree using pre-order traversal (e.g for printing) should print the nodes in ascending order.

All I need is a simple algorithm. I tried a simple insertion algorithm given here (on stackoverflow, but it seems to be incorrect (also tried it on paper));.

The nodes are basically like:

typedef struct testNode{
    int key;
    struct testNode *leftChild;
    struct testNode *rightChild;
}NODE;

Insertion data is just a list of unique integers. I create a node with the int as key and then should add the node to the tree. I have the root node, which starts off as a NULL pointer.

Sorry if anything isn't clear.

Thanks for the help!

EDIT: Based on the algorithm provided below, this is what I came up with:

void insert(NODE **tree,int key){
if(*tree){
    if ((*tree)->key >= key){
        //Insert before this .....
        NODE *temp = createNode(key);
        temp->lc = (*tree);
        (*tree) = temp;

    }
    else if(!(*tree)->rc){
        //Right Child doesn't exist ....
        insert(&(*tree)->lc,key);
    }
    else if((*tree)->rc->key < key){
        //Right child exists and is smaller than spread ... insert left ...
        insert(&(*tree)->lc,key);
    }
    else{
        //Right child exists and is greater than spread ... insert right ...
        insert(&(*tree)->rc,key);
    }
    //If the code as progressed to this point, insertion must have occured, 
            //and the code returns ......   
} else {
    //the **tree pointer points to NULL. Insert here ....
    SPREADNODE *temp = createSpreadNode(spread);
    //temp->lc = (*tree);
    (*tree) = temp;
}
}
GCon
  • 1,397
  • 3
  • 16
  • 33
  • 3
    I hope you've just pasted that struct wrong. – paddy Dec 17 '12 at 23:27
  • When you say "pre-ordered" binary tree, do you mean a BST (that is already ordered) or is this somehow related to a pre-order traversal? – Yaniv Dec 17 '12 at 23:29
  • Yes, the struct was typed in a hurry, I will edit it. Pre-Ordered, in the sense that I should insert nodes in the tree so that when traversing it using Pre-order traversal it visits the nodes in the correct order. – GCon Dec 17 '12 at 23:33
  • Won't compile. A node cannot contain instances of itself. What would sizeof (struct testNode) be? Sizeof (struct Testnode) ? 2* sizeof(testNode) + sizeof(int) ? – wildplasser Dec 18 '12 at 00:12
  • @wildplasser, you will have to forgive me, I have been at this for almost 5 hours. The leftChild and rightChild are in fact pointers. – GCon Dec 18 '12 at 00:31

1 Answers1

2

Think about the definition of a pre-ordered BST: The root node is the smallest element, and its two children or also pre-ordered trees such that the root of the right subtree is larger than any value in the left subtree.

So, one algorithm that would work would be:

  1. If the new node is smaller than the root, make it the new root and point to the existing tree as one of the two child nodes.
  2. If the new node is larger than the root, but smaller than the root of the right subtree, recursively insert it into the left subtree.
  3. Otherwise, recursively insert it into the right subtree.

This isn't likely to produce a very balanced tree, but it should work. I can think of at least one other simple alternative, and there are no doubt ways to make things more balanced that I leave as an exercise to the reader ;-)

Yaniv
  • 991
  • 6
  • 13
  • Thank you for you answer, I will begin implementing immediatly :). In the mean time, let me comment: 1. I have tried this -by itself- by inserting a desending order of integers (i.e. 5 4 3 2 1). It basically creates a linked list though the left child pointers. 2. I thought of this. If a node is smaller than the root of the right subtree, then it should, by definition, be smaller than the root of the left subtree. Why does inserting before the left sub-tree take priority of inserting in the right subtree? – GCon Dec 17 '12 at 23:47
  • Also, and sorry for making a mess of the question: Basically what I have done up to now is traverse the tree using pre-order, and when I reach a >= node (or NULL) insert before that position. How should I proceed to insert? Should I check all the "special" cases (e.g. inserting before a right-child, that has a NULL left sibbling? Inserting in between the right child and it's parent can't lead to a better balanced tree than inserting at the left child, can it? – GCon Dec 17 '12 at 23:56
  • @GCon: I did mention that this would not necessarily lead to a balanced tree. It's quite normal for naive insertion algorithms to lead to "sticks" for certain common input sequences. That does not mean that they are not correct. – Yaniv Dec 18 '12 at 00:05
  • "If a node is smaller than the root of the right subtree, then it should, by definition, be smaller than the root of the left subtree". How do you figure that? – Yaniv Dec 18 '12 at 00:07
  • :) I need some sleep. I meant that if the node to be inserted is smaller than the left subtree it should also be smaller than the right subtree. I have come to the conclusion that the best practice is to prefer the left subtree when smaller than both child nodes. I also think this suffices for the project. Thank you very much for your help! – GCon Dec 18 '12 at 00:35
  • Also, and just in case it helps anyone else, the conclusion for inserting left, if smaller than left (and obviously smaller than right) is that is leads to a "somewhat" balanced tree if the entries occur in random order with a good distribution. – GCon Dec 18 '12 at 00:37
  • just for kicks, is there any specific gain in not predefining which child takes the tree when inserting before the root node? – GCon Dec 18 '12 at 00:40
  • 2
    I suggest you get some sleep. Also: don't touch your computer. First make some drawings with pencil & paper. If you define the problem recursively, you need only two conditions, IICC. The outcome is simple: either you replace the rootnode ("pushdown") or you replace the L or R subtrees, and/or recurse. Draw it before you touch the keyboard! – wildplasser Dec 18 '12 at 00:41
  • @wildplasser, I am drawing at the moment :) Thank you for your advice. I have implemented 3 conditions: Either pushdown, if smaller than RC->key insert(node->left), else insert(node->right). Since sleep deprivation might be getting the better of me, I am trying to figure out (with pen and paper :) what happens when there are missing children in a node? I can't compare to decide where I insert. Should I check if LC / RC are missing and just insert there? – GCon Dec 18 '12 at 00:53
  • 1
    When you start thinking recursively; a missing child node is not different from an empty rootnode. (In both cases, it won't do any harm to "pushdown" a NULL node/pointer) – wildplasser Dec 18 '12 at 00:57
  • @wildplasser, I edited my question with some simple code. How can I choose which child to use when calling itself for recursion? – GCon Dec 18 '12 at 01:00
  • @gcon: the most critical test involves the right child: if the node's value is less that the right child, it must be inserted into the left subtree. If there is no right child, you can also insert into the left subtree. And if there is no left *and* no right child, you can also insert into the left subtree. So basically, insert into the left subtree unless the right child exists *and* is less than the value of the new item. (again, this won't help with balance but does ensure a properly-formed tree) – Yaniv Dec 18 '12 at 01:06
  • So, I DO have to take into account all the possible combination of children in order to figure out how to continue the recursion? – GCon Dec 18 '12 at 01:11
  • 1
    @GCon: seems to me a pre-order tree insertion is harder than an in-order insertion because you don't learn everything you need to know about the recursion just by examining the current node; I could be wrong, but it seems to me that examination of the children in order to make the correct recursive choice cannot be avoided. – Yaniv Dec 18 '12 at 01:16
  • @Yaniv Please forgive my last comment, you were crystal clear and I was out of it. The code has been updated and works perfectly (in the sense that preorder traversal prints all random entries in order). Thank you again! – GCon Dec 18 '12 at 01:28
  • @Yaniv, the code works, but after running it with different inputs, always yields a linked list though the left child. This occurs because an insertion through the right child only occurs if there is a right child ... so never happens. – GCon Dec 18 '12 at 01:45
  • @GCon, that's correct, if you use this algorithm to construct a pre-order tree you will always end up with a stick (which is a valid pre-order tree, though). This does not make it invalid though. Constructing balanced trees is in most cases nontrivial, so if you want some better performance guarantees you'll probably have to do some more thinking. A better question is, though: why do you want a pre-ordered tree in the first place? This is a pretty unusual data structure for actual applications... – Yaniv Dec 18 '12 at 02:34
  • It is part of a larger assignment for a Data Structures class at university. The preorder part was explicitly stated. So, technically, is is academic, not real world. – GCon Dec 18 '12 at 08:12