Sie sind auf Seite 1von 20

Binary Search Tree

1. Abstract idea of a tree: A tree is another data structure that you can use to store pieces of information, or rather, a bunch of elements. Here, we'll consider elements that each have a key (that identifies the element) and a value (that is the data for an element), however, we'll ignore the value part for now. Here is an example of a tree whose keys are letters:
tree ---j / \ / a f \ h k

<-- root \ z <-- leaves

Tree Vocabulary

Let's now introduce some vocabulary with our sample tree... The element at the top of the tree is called the root. The elements that are directly under an element are called its children. The element directly above something is called its parent. For example, a is a child of f and f is the parent of a. Finally, elements with no children are called leaves. Is k a leaf? Who is its parent? How many parents can elements have? Aside: If you were to draw the picture above upside down, it would look like a real tree, with the leaves at the top and the root at the bottom...However, we usually draw tree data structures as we've done above.
Recursive Data Structure

A tree can be viewed as a recursive data structure. Why? Remember that recursive means that something is defined in terms of itself. Here, this means that trees are made up of subtrees. For example, let's look at our tree of letters and examine the part starting at f and everything under it...
tree ---j / \ \ h

f a /

k \ z

Doesn't it look like a tree itself? In this subtree, what is f (recall our vocabulary)? What about just z? Doesn't it look like a subtree?
Binary Trees

We can talk about trees where the number of children that any element has is limited. In the tree above, no element has more than 2 children. For the rest of this example, we will enforce this to be the case. A tree whose elements have at most 2 children is called a binary tree. Since each element in a binary tree can have only 2 children, we typically name them the left and right child.
Ordering of Tree?

The structure of a tree is hierarchical, meaning that things are ordered above or below other things. For example, the army is hierarchical, with generals above colonels, and colonels above lieutenants, etc. Despite the hierarchical order of the structure of the tree, the order enforced on elements in the tree will depend on how we use the tree.
Binary Search Tree

The tree that we presented above actually has an enforced order.

First, remember that the letters in the tree are keys for the elements held in the tree. For any element, keys to its left are less than its own key. Also, keys to its right are greater than its own key. E.g.:
f<j f / j \ k>j k

Note that this is true for every element. Aside: What about the equality case, i.e., two elements with the same element? Does the fact that elements have keys suggest an answer? A tree with this ordering property AND that is binary is called a binary search tree. Why? Because in order to search for an element (with a specific key) in such a tree, you only need to make a series of binary (i.e., go left or right) decisions. For example, to find h starting from the tree's root...
tree ---j / \ / a f \ h k

<-- root \ z <-- leaves

1. Key h is less than j, so go left. 2. Key h is greater than f, so go right. 3. Found h!

Moreover, searching in a binary search tree is easier than in an unordered tree since no backtracking is required. Note: Backtracking is needed when traversing all the elements in a tree.
2.

Tree Operations:

Here are the operations that we will concern ourselves with for this binary search tree. You may need others for a particular use of the tree.
o
Add:

Places an element in the tree, maintaining the correct order. For example, Add(tree,
tree ---j / \ \ h \ i i)

gives:

<-- root k \ z <-- new leaf

f a /

Note that i < j, so we go to left of j. Then, i > f so we go to right of f. Finally, i > h so we go to right of h, where there is an empty slot to put it.

To produce that tree, we added the elements in the following order:


j, f, k, a, h, z, i

What would the tree have looked like if we added these elements in the order: j, k, z, f, h, i, a? What about the order: a, z, k, f, i, h, j? Note: With the same set of elements, different orders of adding can give you the same tree OR different trees! Since we typically use elements, each with a unique key, it is reasonable to require that there are no two elements in the tree with the same key.
o
IsMember:

Reports whether some element is in the tree. For example, IsMember(tree, a) should give a true value and IsMember(tree, y) should give a false value.
o
Print:

We also want something to print out the elements in the tree in key order (ascending or descending). Printing out the elements in ascending order would give:
a f h i j k z

What would the list of elements be in descending order? Does what the tree looks like affect the order that things are printed out? We may want more operations depending on how we'll use the tree. Which of the operations need entire elements and which require only keys?
2.

Adding Algorithm (with order preservation): Let's consider an algorithm for adding an element to a binary search tree. Adding an element requires searching for the proper place to put the new element, so that the binary search order will be preserved. We've already seen that no backtracking is needed when searching the tree, so our algorithm can use iteration (looping)--it does not require recursion. Here is an outline of such an algorithm (that assumes there is at least one element in the tree):
Add(tree, element)

[iterative]

while (not done) if (element's key < root's key) if (no left child of root)

put new element left of root done! else tree = left subtree else if (element's key > root's key) if (no right child of root) put new element right of root done! else tree = right subtree else do whatever you do when key is already in tree done!

When the algorithm begins, it is given the entire tree. As it continues to search, it works it's way to lower and lower subtrees.
3.

Tree implementation in C: We want to implement a binary search tree that has the above properties and operations in C. Although we could use an array to implement a tree, we'll use an implementation more akin to a linked list... Recall that our trees store elements with both a key and a value. For simplicity, assume the values are just integers (a count, e.g.). The types needed for elements are thus:
typedef char treeKeyT; typedef int treeValueT; typedef struct { treeKeyT key; treeValueT value; } treeElementT;

Now, how will these elements be stored in a tree? Like a linked list, elements will be stored in nodes. Furthermore, a node will have to keep track of its element's immediate children. How will a node keep track of the left and right child? Answer: Like a linked list, nodes will point to one another in the tree-each node will point to the left and right child's node. The type needed for a node is thus:

typedef struct treeNodeTag { treeElementT element; struct treeNodeTag *left, *right; } treeNodeT;

When there is no left or right child, of course, we'll make the corresponding pointers NULL. Now, let's return to our original tree, but view it as if it was made up of these C treeNodeTs...
----|j | |5 | |---| | | | /---\ v ----|f | |30 | |---| | | | /---\ v ----|k | |13 | |---| |0| | ----\

v ----|a | |100| |---| |0|0| -----

v ----|h | |50 | |---| |0|0| -----

v ----|z | |1 | |---| |0|0| -----

While these nodes suffice to keep track of all the elements, what do we need to keep track of the whole tree? Answer: We need a pointer to the root of the tree!
Special case: Empty Tree

What about when the tree is empty. How do we represent an empty tree? Answer: There are no nodes, so the pointer to the root should be NULL.
4.

Organization of data types for a tree: We have already thought a little about the types needed for our tree. Let's now think about how to organize those types and use them with an ADT/CDT design.

As usual, we'll put our tree data structure in its own module, creating the source files tree.h and tree.c. The types for a key, value and element should be part of the interface in tree.h. The type for a node is an implementation detail, and thus, goes in tree.c. Finally, we need something that holds all the information needed to keep track of the tree. We saw that this should be a pointer to the node at the root of the tree. Since this pointer has to do with the implementation of the tree, we put it in the concrete type, struct treeCDT:
typedef struct treeCDT { treeNodeT *root; } treeCDT;

which goes in tree.c. In the interface (tree.h), we must fill in what the abstract type is as follows:
typedef struct treeCDT *treeADT;

Finally, we have:
tree.h -----typedef char treeKeyT; tree.c -----#include "tree.h"

typedef struct treeNodeTag { treeElementT element; typedef int treeValueT; struct treeNodeTag *left, *right; typedef struct { } treeNodeT; treeKeyT key; treeValueT value; } treeElementT; typedef struct treeCDT *treeADT; typedef struct treeCDT { treeNodeT *root; } treeCDT;

5.

Tree functions:

Now that we've finished the types, let's discuss the tree functions. Based on the tree operations we mentioned above, the tree functions we'll need are: For general tree operations: o TreeAdd() o TreeIsMember() o TreePrint() Because we are programming in C (setup/cleanup): o TreeCreate() o TreeDestroy() We've briefly discussed the types and functions needed. Before we discuss implementing any functions, take a look at the test program treetest.c to see how the types and functions (from the tree module) will be used. Let's now consider what is needed for 2 tree functions: TreeCreate() and TreeAdd().
o
TreeCreate:

Prototype:
treeADT TreeCreate(void);

Remember that a treeADT is just a pointer, so TreeCreate() will have to create a treeCDT, initialize it, and give back a pointer to the CDT.
o
TreeAdd:

Prototype:
void TreeAdd(treeADT tree, treeElementT element);

We've already discussed an algorithm for adding an element.

Remember we assumed adding an element to a tree with at least one element, so adding to an empty tree is a special case...How do we detect that case and then what do we do? Also remember that since no backtracking is needed when finding where to place an element in a binary search tree, we wrote the algorithm using iteration. Go ahead and implement these functions!
6.

Other functions: If desired, you may write the other functions needed for the binary search tree. Fill in the prototypes for the rest of the tree functions:
treeADT return-type return-type void TreeCreate(void); TreeDestroy(parameters); TreeIsMember(parameters); TreeAdd(treeADT tree, treeElementT element); return-type TreePrint(parameters); ...

For functions that refer to elements, which need a complete element and which only need take a key as parameter? After writing the prototypes, write definitions for each function. Note: Remember to place the prototypes in the interface (tree.h) and function definitions in the implementation (tree.c) of your tree module. Test your implementation with the sample program treetest.c that uses elements that are letter/count pairs. A Makefile is also available for the program.
/ *********************************************************************** **************/ /* C to implement and Creation of Binary search tree */ /* Download more programs at http://sourcecode4u.com/ */

/ *********************************************************************** **************/ #include<stdio.h> #include<conio.h> struct tree { int data; struct tree *left; struct tree *right; }; struct tree *create(); void preorder(struct tree *); void inorder(struct tree *); void postorder(struct tree *); struct tree *create() { struct tree *p,*root; int m,x; char s; root=(struct tree *)malloc(sizeof(struct tree)); printf("\nenter the value of the main root"); scanf("%d",&m); root->data=m; root->left=NULL; root->right=NULL; printf("\nenter n to stop creation of the binary search tree"); fflush(stdin); scanf("%c",&s); while(s!='n') { p=root; printf("\nenter the value of the newnode"); fflush(stdin); scanf("%d",&x); while(1) { if(x<p->data) { if(p->left==NULL) { p->left=(struct tree *)malloc(sizeof(struct tree)); p=p->left; p->data=x; p->right=NULL; p->left=NULL; break; } else p=p->left; } else { if(p->right==NULL) { p->right=(struct tree *)malloc(sizeof(struct tree));

} else p=p->right;

p=p->right; p->data=x; p->right=NULL; p->left=NULL; break;

} void preorder(struct tree *p) { if(p!=NULL) { printf("%d ",p->data); preorder(p->left); preorder(p->right); } } void inorder(struct tree *p) { if(p!=NULL) { inorder(p->left); printf("\t%d",p->data); inorder(p->right); } } void postorder(struct tree *p) { if(p!=NULL) { postorder(p->left); postorder(p->right); printf("\t%d",p->data); } } void main() { int h; struct tree *root; while(1) { printf("\nenter 1. for creation of the binary search tree"); printf("\nenter 2. for preorder traversal"); printf("\nenter 3. for inorder traversal"); printf("\nenter 4. for postorder traversal"); printf("\nenter 5. for exit"); printf("\nenter your choice"); scanf("%d",&h); switch(h)

} return(root);

} } printf("\nwant to continue"); fflush(stdin); scanf("%c",&s);

} } }

case 1: root=create(); break; case 2: preorder(root); break; case 3: inorder(root); break; case 4: postorder(root); break; case 5: exit(0); default: printf("\nentered a wrong choice");

Binary Search Trees (BSTs) in C#


By FERHAT | Published: JUNE 14, 2011 A Binary Search Tree (also known as a BST) is a data structure that contains ordered nodes. Each node contains an element, a left node and a right node. The nodes on the left are all nodes that contain values lower than the element and the nodes on the right contain values higher than the element.

Binary Search Tree Nodes can be found very fast, in logarithmic time on average. Suppose you would search for a node with a value of 9 in the above tree. This would take 2 steps. Searching starts at the root of the tree which is the node with a value of 12. 1. 9 is smaller than 12 so look in the left subtree 2. 9 is larger than 7 so look in the right subtree, youve found 9!

The below example is a C# console application which illustrates BSTs. Major credits to Mark Allen Weiss for his explanation on BSTs in Data Structures & Problem Solving Using Java. TreeNode class to represent a node
?

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
?

class TreeNode<T> { public T Element { get; set; } public TreeNode<T> Left { get; set; } public TreeNode<T> Right { get; set; } public TreeNode(T element) { this.Element = element; } public override string ToString() { string nodeString = "[" + this.Element + " "; // Leaf node if (this.Left == null && this.Right == null) { nodeString += " (Leaf) "; } if (this.Left != null) { nodeString += "Left: " + this.Left.ToString(); } if (this.Right != null) { nodeString += "Right: " + this.Right.ToString(); } nodeString += "] "; return nodeString;

} }

BinarySearchTree class to represent a BST

1 2 3 4

class BinarySearchTree<T> { public TreeNode<T> Root { get; set; } public BinarySearchTree()

5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50

{ }

this.Root = null;

public void Insert(T x) { this.Root = Insert(x, this.Root); } public void Remove(T x) { this.Root = Remove(x, this.Root); } public void RemoveMin() { this.Root = RemoveMin(this.Root); } public T FindMin() { return ElementAt(FindMin(this.Root)); } public T FindMax() { return ElementAt(FindMax(this.Root)); } public T Find(T x) { return ElementAt(Find(x, this.Root)); } public void MakeEmpty() { this.Root = null; } public bool IsEmpty() { return this.Root == null; } private T ElementAt(TreeNode<T> t) { return t == null ? default(T) : t.Element; } private TreeNode<T> Find(T x, TreeNode<T> t) { while (t != null) { if ((x as IComparable).CompareTo(t.Element) < 0)

51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96

t = t.Left; } else if ((x as IComparable).CompareTo(t.Element) > 0) { t = t.Right; } else { return t; }

return null;

private TreeNode<T> FindMin(TreeNode<T> t) { if (t != null) { while (t.Left != null) { t = t.Left; } } return t; } private TreeNode<T> FindMax(TreeNode<T> t) { if (t != null) { while (t.Right != null) { t = t.Right; } } return t;

protected TreeNode<T> Insert(T x, TreeNode<T> t) { if (t == null) { t = new TreeNode<T>(x); } else if ((x as IComparable).CompareTo(t.Element) < 0) { t.Left = Insert(x, t.Left); } else if ((x as IComparable).CompareTo(t.Element) > 0) { t.Right = Insert(x, t.Right);

97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142

} else { }

throw new Exception("Duplicate item");

return t; } protected TreeNode<T> RemoveMin(TreeNode<T> t) { if (t == null) { throw new Exception("Item not found"); } else if (t.Left != null) { t.Left = RemoveMin(t.Left); return t; } else { return t.Right; } } protected TreeNode<T> Remove(T x, TreeNode<T> t) { if (t == null) { throw new Exception("Item not found"); } else if ((x as IComparable).CompareTo(t.Element) < 0) { t.Left = Remove(x, t.Left); } else if ((x as IComparable).CompareTo(t.Element) > 0) { t.Right = Remove(x, t.Right); } else if (t.Left != null && t.Right != null) { t.Element = FindMin(t.Right).Element; t.Right = RemoveMin(t.Right); } else { t = (t.Left != null) ? t.Left : t.Right; } return t;

public override string ToString() { return this.Root.ToString();

143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
?

} }

The main program

1 2 3 4 5 6 7 8 9 10 11 12 13 14

class Program { static void Main(string[] args) { // Initialize a BST which will contain integers BinarySearchTree<int> intTree = new BinarySearchTree<int>(); Random r = new Random(DateTime.Now.Millisecond); string trace = ""; // Insert 5 random integers into the tree for (int i = 0; i < 5; i++) { int randomInt = r.Next(1, 500); intTree.Insert(randomInt); trace += randomInt + " "; }

15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37

// Find the largest value in the tree Console.WriteLine("Max: " + intTree.FindMax()); // Find the smallest value in the tree Console.WriteLine("Min: " + intTree.FindMin()); // Find the root of the tree Console.WriteLine("Root: " + intTree.Root.Element); // The order in which the elements were added to the tree Console.WriteLine("Trace: " + trace); // A textual representation of the tree Console.WriteLine("Tree: " + intTree); Console.ReadLine(); } }

Output:
?

1 2 3 4 5

Max: 452 Min: 17 Root: 245 Trace: 245 432 100 452 17 Tree: [245 Left: [100 Left: [17

(Leaf) ] ] Right: [432 Right: [452

(Leaf) ] ]]

Graphical representation of the resulting BST

Resulting BST The code belonging to the above example can be downloaded from here.

This entry was posted in C# and tagged binary search tree, binary tree, BST, c#, c-sharp, data structure,element, generics, leaf, log n, logarithmic time, node, programming, root node, tree. Bookmark the permalink.Post a comment or leave a trackback: Trackback URL.