Trees in Data Structures
What is a Tree A tree is a finite nonempty set of elements. It is an abstract model of a hierarchical structure. Consists of nodes with a parent-child relation. What is a Tree Computer Sales R&D Manufacturing Laptops Desktops US International Europe Asia Canada Applications: Organization charts File systems Programming environments
Tree Terminology Root: node without parent (A) Siblings: nodes share the same parent External node (leaf ): node without children (E, I, J, K, G, H, D) Internal node: node with at least one child (A, B, C, F) A B D C G H E F I J K Ancestors of a node: parent, grandparent, grand-grandparent, etc. Descendant of a node: child, grandchild, grand-grandchild, etc. Degree of a node: the number of its children Depth of a node: number of ancestors Height of a tree: maximum depth of any node (3) Degree of a tree: the maximum number of its node. Subtree: tree consisting of a node and its descendants
Tree Properties Number of nodes : Height : Root Node : Leaves : C D G E F I H Number of nodes : Height : Root Node : Leaves : Internal nodes : Ancestors of H : Descendants of B : Siblings of E : Right subtree of A : Degree of this tree :
We call the children of an internal node left child and right child Binary Tree A binary tree is a tree with the following properties: Each internal node has at most two children (degree of two) The children of a node are an ordered pair A B C F G D E H I Alternative recursive definition: a binary tree is either a tree consisting of a single node, OR a tree whose root has an ordered pair of children, each of which is a binary tree We call the children of an internal node left child and right child
Binary Tree Representation right child left A B C B right child left C right child left D E F struct tree { int data; struct tree *left; struct tree *right }; D right child left E right child left F right child left
Full binary tree of depth 3 1 2 3 7 5 9 4 8 6 Complete binary tree A B Skewed Binary Tree E C D 1 2 3 7 5 11 4 10 6 9 8 15 14 13 12 Full binary tree of depth 3
Binary Tree Traversal In a traversal of a binary tree, each element of the binary tree is visited exactly once. During the visit of an element, all action (make a clone, display, evaluate the operator, etc.) with respect to this element is taken. Tree Traversal Methods Preorder Inorder Postorder Level order Ptr points to the root of the subtree being traversed.
Preorder Traversal preOrder(treePointer ptr) { if (ptr != NULL) print(t); preOrder(ptr->leftChild); preOrder(ptr->rightChild); } a b c Ptr points to the root of the subtree being traversed.
Preorder Example (Visit = print) preOrder(treePointer ptr) { if (ptr != NULL) print(t); preOrder(ptr->leftChild); preOrder(ptr->rightChild); } a b c d e f g h i j a b d g h e i c f j
Preorder Of Expression Tree + a b - c d e f * / / * + a b - c d + e f Gives prefix form of expression!
Inorder Traversal inOrder(treePointer ptr) { if (ptr != NULL) inOrder(ptr->leftChild); print(ptr); inOrder(ptr->rightChild); } a b c b a c
Inorder Traversal inOrder(treePointer ptr) { if (ptr != NULL) inOrder(ptr->leftChild); print(ptr); inOrder(ptr->rightChild); } a b c d e f g h i j g d h b e i a f j c
Inorder Of Expression Tree + a b - c d e f * / e a + b * c d / f - Gives infix form of expression (sans parentheses)!
Postorder Traversal void postOrder(treePointer ptr) { if (ptr != NULL) postOrder(ptr->leftChild); postOrder(ptr->rightChild); print(t); } a b c b c a
Postorder Traversal void postOrder(treePointer ptr) { if (ptr != NULL) postOrder(ptr->leftChild); postOrder(ptr->rightChild); print(t); } a b c d e f g h i j g h d i e b j f c a
Postorder Of Expression Tree + a b - c d e f * / a b + c d - * e f / Gives postfix form of expression!
Level Order Let ptr be a pointer to the tree root. while (ptr != NULL) { visit node pointed at by ptr and put its children on a FIFO queue; if FIFO queue is empty, set ptr = NULL; otherwise, delete a node from the FIFO queue and call it ptr; } a b c d e f g h i j a b c d e f g h i j
Binary Search Tree Data Structure Binary tree property each node has 2 children result: storage is small operations are simple average depth is small Search tree property all keys in left subtree smaller than root’s key all keys in right subtree larger than root’s key easy to find any given key Insert/delete by changing links 4 12 10 6 2 11 5 8 14 13 7 9 A binary search tree is a binary tree in which all nodes in the left subtree of a node have lower values than the node. All nodes in the right subtree of a node have higher value than the node. It’s like making that recursion into the data structure! I’m storing integers at each node. Does everybody think that’s what I’m _really_ going to store? What do I need to know about what I store? (comparison, equality testing)
Example and Counter-Example 8 5 5 11 4 8 2 7 6 10 18 1 7 11 Why is the one on the left a BST? It’s not complete! (B/c BSTs don’t need to be complete) Why isn’t the one on the right a BST? Three children of 5 20 has a left child larger than it. What’s wrong with 11? Even though 15 isn’t a direct child, it _still_ needs to be less than 11! 4 15 20 3 NOT A BINARY SEARCH TREE 21 BINARY SEARCH TREE
Complete Binary Search Tree (aka binary heap): Links are completely filled, except possibly bottom level, which is filled left-to-right. 8 5 15 3 7 9 17 1 4 6
In-Order Traversal 10 visit left subtree visit node visit right subtree What does this guarantee with a BST? 5 15 2 9 20 Anyone notice anything interesting about that in-order listing? Everything in the left subtree is listed first. Then the root. Then everything in the right subtree. OK, let’s work out the code to make the in-order listing. Is there an iterative version that doesn’t use its own stack? Not really, no. So, recursion is probably OK here. Anyway, if the tree’s too deep for recursion, you must have a huge amount of data. If (n != null) inorder(n->left) cout << n inorder(n->right) 7 17 30 In order listing: 25791015172030
Iterative Find Recursive Find Node * find(Comparable key, Node * t) { while(t!=NULL && t->key != key) if (key < t->key) t = t->left; else t = t->right; } return t; Node * find(Comparable key, Node * t) { if (t == NULL) return NULL; else if (key < t->key) return find(key, t->left); else if (key > t->key) return find(key, t->right); else return t; } Now, let’s try finding a node. Find 9. This time I’ll supply the code. This should look a _lot_ like binary search! How long does it take? Log n is an easy answer, but what if the tree is very lopsided? So really, this is worst case O(n)! A better answer is theta of the depth of the node sought. If we can bound the depth of that node, we can bound the length of time a search takes.
Insert void insert(Comparable x, Node * t) { Concept: if ( t == NULL ) { t = new Node(x); } else if (x < t->key) { insert( x, t->left ); } else if (x > t->key) { insert( x, t->right ); } else { // duplicate -handling is app- dependent } Concept: Proceed down tree as in Find If new key not found, then insert a new node at last spot traversed Now, let’s try finding a node. Find 9. This time I’ll supply the code. This should look a _lot_ like binary search! How long does it take? Log n is an easy answer, but what if the tree is very lopsided? So really, this is worst case O(n)! A better answer is theta of the depth of the node sought. If we can bound the depth of that node, we can bound the length of time a search takes.
FindMin, FindMax 20 9 2 15 5 10 30 7 17 Node* min(Node * t) { if(t->left==NULL) return(t); else min(t->left); } Node* max(Node * t) { if(t->right==NULL) return(t); else max(t->right); }
Successor Node Next larger node in this node’s subtree 20 9 2 15 5 10 30 7 17 Node * succ(Node * t) { if (t->right == NULL) return NULL; else return min(t->right); } Here’s a little digression. Maybe it’ll even have an application at some point. Find the next larger node in 10’s subtree. Can we define it in terms of min and max? It’s the min of the right subtree! How many children can the successor of a node have?
Predecessor Node Next smaller node in this node’s subtree 10 Node * pred(Node * t) { if (t->left == NULL) return NULL; else return max(t->left); } 5 15 2 9 20 Predecessor is just the mirror problem. 7 17 30
Deletion 10 5 15 2 9 20 And now for something completely different. Let’s say I want to delete a node. Why might it be harder than insertion? Might happen in the middle of the tree instead of at leaf. Then, I have to fix the BST. 7 17 30 Why might deletion be harder than insertion?
Deletion - Leaf Case Delete(17) 10 5 15 2 9 20 7 17 30 Alright, we did it the easy way, but what about real deletions? Leaves are easy; we just prune them. 7 17 30
Deletion - One Child Case Delete(15) 10 5 15 2 9 20 Single child nodes we remove and… Do what? We can just pull up their children. Is the search tree property intact? Yes. 7 30
Deletion - Two Child Case Replace node with descendant whose value is guaranteed to be between left and right subtrees: the successor 30 9 2 20 5 10 7 Ah, now the hard case. How do we delete a two child node? We remove it and replace it with what? It has all these left and right children that need to be greater and less than the new value (respectively). Is there any value that is guaranteed to be between the two subtrees? Two of them: the successor and predecessor! So, let’s just replace the node’s value with it’s successor and then delete the succ. Delete(5) Could we have used predecessor instead?
Delete Code void delete(Comparable key, Node *& root) { Node *& handle(find(key, root)); Node * toDelete = handle; if (handle != NULL) { if (handle->left == NULL) { // Leaf or one child handle = handle->right; delete toDelete; } else if (handle->right == NULL) { // One child handle = handle->left; } else { // Two children successor = succ(root); handle->data = successor->data; delete(successor->data, handle->right); } 30 9 2 20 5 10 7 Here’s the code for deletion using lots of confusing reference pointers BUT no leaders, fake nodes. The iterative version of this can get somewhat messy, but it’s not really any big deal.
Balance factor = height Of Left Subtree – height Of Right Subtree AVL Tree The AVL tree was introduced in the year of 1962 by G.M. Adelson-Velsky and E.M. Landis. AVL tree is a self balanced binary search tree. AVL trees are height balancing binary search tree. A binary tree is said to be balanced, if the difference between the heights of left and right subtrees of every node in the tree is either -1, 0 or +1. In an AVL tree, every node maintains a extra information known as balance factor Balance factor = height Of Left Subtree – height Of Right Subtree
Every AVL Tree is a binary search tree but all the Binary Search Trees need not to be AVL trees. The above tree is a binary search tree and every node is satisfying balance factor condition. So this tree is said to be an AVL tree.
AVL Tree Rotations Rotation is the process of moving the nodes to either left or right to make tree balanced.
Single Left Rotation (LL Rotation)
Single Right Rotation (RR Rotation)
Left Right Rotation (LR Rotation)
Right Left Rotation (RL Rotation)
Insertion Operation in AVL Tree Step 1: Insert the new element into the tree using Binary Search Tree insertion logic. Step 2: After insertion, check the Balance Factor of every node. Step 3: If the Balance Factor of every node is 0 or 1 or -1 then go for next operation. Step 4: If the Balance Factor of any node is other than 0 or 1 or -1 then tree is said to be imbalanced. Then perform the suitable Rotation to make it balanced. And go for next operation.
Insertion – Insert 1 to 8 in AVL Tree
Insertion – Insert 1 to 8 in AVL Tree
Insertion – Insert 1 to 8 in AVL Tree
Insertion – Insert 1 to 8 in AVL Tree
Insertion – Insert 1 to 8 in AVL Tree
Splay Tree Splay Tree is a self - adjusted Binary Search Tree in which every operation on an element rearrange the tree so that the element is placed at the root position of the tree. Splaying an element is the process of bringing it to the root position by performing suitable rotation operations. That means the splaying operation automatically brings more frequently used elements closer to the root of the tree. principle of locality;80-20 “rule” – 80% of the accesses are to 20% of the data
Rotations in Splay Tree 1. Zig Rotation 2. Zag Rotation 3. Zig - Zig Rotation 4. Zag - Zag Rotation 5. Zig - Zag Rotation 6. Zag - Zig Rotation
Zig Rotation The Zig Rotation in a splay tree is similar to the single right rotation in AVL Tree rotations.
Zag Rotation The Zag Rotation in a splay tree is similar to the single left rotation in AVL Tree rotations.
Zig-Zig Rotation The Zig-Zig Rotation in a splay tree is a double zig rotation. In zig-zig rotation every node moves two position to the right from its current position.
Zag-Zag Rotation The Zag-Zag Rotation in a splay tree is a double zag rotation. In zag-zag rotation every node moves two position to the left from its current position.
Zig-Zag Rotation The Zig-Zag Rotation in a splay tree is a sequence of zig rotation followed by zag rotation. In zig-zag rotation every node moves one position to the right followed by one position to the left from its current position.
Zag-Zig Rotation The Zag-Zig Rotation in a splay tree is a sequence of zag rotation followed by zig rotation. In zag-zig rotation every node moves one position to the left followed by one position to the right from its current position.
2 1 3 4 5 6 (6) 2 1 3 6 5 4 zig-zig zig-zig 1 6 3 2 5 4 zig 6 1 3 2 5 4
… 4 splayed out! 6 1 4 3 5 2 zig-zag 6 1 4 3 5 2
Find(T) Find(R)
Splay Tree Insert and Delete Insert x Insert x as normal then splay x to root. Delete x Splay x to root and remove it. (note: the node does not have to be a leaf or single child node like in BST delete.) Two trees remain, right subtree and left subtree. Splay the max in the left subtree to the root Attach the right subtree to the new root of the left subtree.
Splay Tree Insert and Delete Inserting in order 1,2,3,…,8 Without self-adjustment 1 2 3 4 5 6 7 8 O(n2) time for n Insert
Splay Tree Insert and Delete With Self-Adjustment Splay Tree Insert and Delete 2 1 ZigFromRight 1 2 1 3 ZigFromRight ZigFromRight 2 1 3 4 Each Insert takes O(1) time therefore O(n) time for n Insert!!
Splay Tree Insert and Delete 10 15 5 20 13 8 2 9 6 splay remove Splay (zig) attach (Zig-Zag)
B-Tree B-Tree was developed in the year of 1972 by Bayer and McCreight with the name Height Balanced m-way Search Tree. Later it was named as B-Tree. B-Tree is a self-balanced search tree with multiple keys in every node and more than two children for every node. Here, number of keys in a node and number of children for a node is depend on the order of the B-Tree. Every B-Tree has order. A B-tree is a tree data structure that keeps data sorted and allows searches, insertions, and deletions in logarithmic amortized time. Unlike self-balancing binary search trees, it is optimized for systems that read and write large blocks of data. It is most commonly used in database and file systems.
B-Tree Properties of B-Tree B-Tree of Order m has the following properties... Property #1 - All the leaf nodes must be at same level. Property #2 - All nodes except root must have at least [m/2]-1 keys and maximum of m-1 keys. Property #3 - All non leaf nodes except root (i.e. all internal nodes) must have at least m/2 children. Property #4 - If the root node is a non leaf node, then it must have at least 2 children. Property #5 - A non leaf node with n-1 keys must have n number of children. Property #6 - All the key values within a node must be in Ascending Order.
B-Tree
B-Tree Search Operation in B-Tree Read the search element from the user Compare, the search element with first key value of root node in the tree. If both are matching, then display "Given node found!!!" and terminate the function If both are not matching, then check whether search element is smaller or larger than that key value. If search element is smaller, then continue the search process in left subtree. If search element is larger, then compare with next key value in the same node and repeate step 3, 4, 5 and 6 until we found exact match or comparision completed with last key value in a leaf node. If we completed with last key value in a leaf node, then display "Element is not found" and terminate the function.
B-Tree Insertion Operation in B-Tree Check whether tree is Empty. If tree is Empty, then create a new node with new key value and insert into the tree as a root node. If tree is Not Empty, then find a leaf node to which the new key value cab be added using Binary Search Tree logic. If that leaf node has an empty position, then add the new key value to that leaf node by maintaining ascending order of key value within the node. If that leaf node is already full, then split that leaf node by sending middle value to its parent node. Repeat the same until sending value is fixed into a node. If the splitting is occurring to the root node, then the middle value becomes new root node for the tree and the height of the tree is increased by one.
B-Tree Insertion Operation in B-Tree
B-Tree Insertion Operation in B-Tree
B-Tree Insertion Operation in B-Tree
B-Tree Insertion Operation in B-Tree
B-Tree Insertion Operation in B-Tree
B-Tree Insertion Operation in B-Tree
B-Tree Insertion Operation in B-Tree
B-Tree Insertion Operation in B-Tree
B-Tree Insertion Operation in B-Tree
B-Tree Delete Operation in B-Tree
B-Tree Delete Operation in B-Tree – Delete 11
B-Tree Delete Operation in B-Tree – Delete 11
B-Tree Delete Operation in B-Tree – Delete 11
B-Tree Delete Operation in B-Tree – Delete 11
B-Tree Delete Operation in B-Tree – Delete 11
B-Tree Delete Operation in B-Tree – Delete 10 If the key is already in a leaf node, and removing it doesn’t cause that leaf node to have too few keys, then simply remove the key to be deleted.
B-Tree Delete Operation in B-Tree – Delete 10
B-Tree Delete Operation in B-Tree – Delete 14 If key k is in node x and x is an internal node, there are three cases to consider:
B-Tree Delete Operation in B-Tree – Delete 14
B-Tree Delete Operation in B-Tree – Delete 14
B-Tree https://www.cs.usfca.edu/~galles/visualization/BTree.html Insertion Operation in B-Tree https://www.cs.usfca.edu/~galles/visualization/BTree.html