Trees Introduction Applications Tree types Binary tree Tree
Trees • Introduction • Applications • Tree types • Binary tree • Tree traversal • Expression tree • General tree 1
Tree Data Structure • Linear Data Structures: Array, Stack, Queue, and Linked List. • Non-Linear Data Structures: Tree and Graph. • In order to perform any operation in a linear data structure, the time complexity increases with the increase in the data size. But, it is not acceptable in today's computational world. 2
Applications of tree data structure • Tree is hierarchical (or non-linear) data structure. • Storing information that naturally forms a hierarchy. For example, the file system on a computer: file system • Store hierarchical data, like folder structure, organization structure, XML/HTML data. • If we organize keys in form of a tree (with some ordering e. g. , BST), we can search for a given key in moderate time (quicker than Linked List and slower than arrays). Self-balancing search trees like AVL and Red-Black trees guarantee an upper bound of O(Logn) for search. • We can insert/delete keys in moderate time (quicker than Arrays and slower than Unordered Linked Lists). Self-balancing search trees like AVL and Red-Black trees guarantee an upper bound of O(Logn) for insertion/deletion. • Like Linked Lists and unlike Arrays, Pointer implementation of trees don’t have an upper limit 3 on number of nodes as nodes are linked using pointers.
Applications of tree data structure • Binary Search Tree is a tree that allows fast search, insert, delete on a sorted data. It also allows finding closest item • Heap is a tree data structure which is implemented using arrays and used to implement priority queues. • B-Tree and B+ Tree : They are used to implement indexing in databases. • Syntax Tree: Used in Compilers. • Trie : Used to implement dictionaries with prefix lookup. • Suffix Tree : For quick pattern searching in a fixed text. • Spanning Trees and shortest path trees are used in routers and bridges respectively in computer networks. 4
Tree • Node: A node is an entity that contains a key or value and pointers to its child nodes. • Edge: It is the link between any two nodes. • Root: It is the topmost node of a tree. • Height of a Node: The height of a node is the number of edges from the node to the deepest leaf (ie. the longest path from the node to a leaf node). • Depth of a Node: The depth of a node is the number of edges from the root to the node. 5
Tree • Height of a Tree: The height of a Tree is the height of the root node or the depth of the deepest node. • Degree of a Node: The degree of a node is the total number of branches of that node. 6
Tree • Forest: A collection of disjoint trees is called a forest. 7
m-ary tree • An m-ary tree (also known as k-ary or k-way tree) is a rooted tree in which each node has no more than m children. An example of a m-ary tree with m=5 • A binary tree is the special case where m = 2, and a ternary tree is another case with m = 3 that limits children to three. 8
m-ary tree • Number of Leaves in k-ary tree 9
Binary Tree • Binary Tree: 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. • A Tree node contains following parts. 1. Data 2. left child 3. right child 10
Binary Tree Applications • For easy and quick access to data • In router algorithms • To implement heap data structure • Syntax tree while b ≠ 0 if a > b a : = a − b else b : = b − a return a 11
Types of Binary Tree Full Binary Tree • A full Binary tree is a special type of binary tree in which every parent node/internal node has either two or no children. i = the number of internal nodes n = be the total number of nodes l = number of leaves λ = number of levels ü The number of leaves is i + 1. ü The total number of nodes is 2 i + 1. ü The number of internal nodes is (n – 1) / 2. ü The number of leaves is (n + 1) / 2. ü The total number of nodes is 2 l – 1. ü The number of internal nodes is l – 1. ü The number of leaves is at most 2λ - 1. 12
Types of Binary Tree Perfect Binary Tree • A perfect binary tree is a type of binary tree in which every internal node has exactly two child nodes and all the leaf nodes are at the same level. ü A perfect binary tree of height h has 2 h + 1 – 1 node. ü A perfect binary tree with n nodes has height log(n + 1) – 1 = Θ(ln(n)). ü A perfect binary tree of height h has 2 h leaf nodes. ü The average depth of a node in a perfect binary tree is Θ(ln(n)). 13
Types of Binary Tree Complete Binary Tree A complete binary tree is just like a full binary tree, but with two major differences • Every level must be completely filled • All the leaf elements must lean towards the left. • The last leaf element might not have a right sibling i. e. a complete binary tree doesn't have to be a full binary tree. Complete Binary Tree Applications • Heap-based data structures • Heap sort 14
Types of Binary Tree Full Binary Tree vs Complete Binary Tree 15
Types of Binary Tree Skewed Binary Tree There are two types of skewed binary tree: left-skewed binary tree and right-skewed binary tree. 16
Types of Binary Tree Balanced Binary Tree It is a type of binary tree in which the difference between the left and the right subtree for each node is either 0 or 1. 17
Types of Binary Tree checking whether a tree is height-balanced: #include <stdio. h> #include <stdlib. h> #define bool int main() { int height = 0; struct node *root = new. Node(1); root->left = new. Node(2); root->right = new. Node(3); root->left = new. Node(4); root->left->right = new. Node(5); if (is. Height. Balanced(root, &height)) printf("The tree is balanced"); else printf("The tree is not balanced"); struct node { int data; struct node *left; struct node *right; }; struct node *new. Node(int data) { struct node *node = (struct node *)malloc(sizeof(struct node)); node->data = data; node->left = NULL; node->right = NULL; return (node); } bool is. Height. Balanced(struct node *root, int *height) { int left. Height = 0, right. Height = 0; int l = 0, r = 0; if (root == NULL) { *height = 0; return 1; } l = is. Height. Balanced(root->left, &left. Height); r = is. Height. Balanced(root->right, &right. Height); *height = (left. Height > right. Height ? left. Height : right. Height) + 1; if ((left. Height - right. Height >= 2) || (right. Height - left. Height >= 2)) return 0; } else return l && r; 18
Numbering nodes in a Binary Tree 19
Binary Tree Representation By Array 20
Binary Tree Representation A node of a binary tree is represented by a structure containing a data part and two pointers to other structures of the same type. struct node { int data; struct node *left; struct node *right; }; struct node *new. Node(int data) { struct node *node = (struct node *)malloc(sizeof(struct node)); node->data = data; node->left = NULL; node->right = NULL; return (node); } 21
Binary Tree • Binary Tree: 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. • A Tree node contains following parts. Implementing by Linked List: 1. Data 2. Pointer to left child 3. Pointer to right child struct node { int data; struct node *left; struct node *right; }; 22
Binary Tree Traversal Methods • Linear data structures like arrays, stacks, queues, and linked list have only one way to read the data. But a hierarchical data structure like a tree can be traversed in different ways. • Tree Traversal: In tree traversal process each node is visited one time • According to this structure, every tree is a combination of a Node carrying data and Two subtrees. • Three traversal methods: inorder, preorder and postorder. 23
Inorder traversal 1. First, visit all the nodes in the left subtree 2. Then visit the root node 3. Visit all the nodes in the right subtree void inorder(struct node* root) { if (root == NULL) return; inorder(root->left); printf("%d ->", root->data); inorder(root->right); } 24
Inorder traversal 1. First, visit all the nodes in the left subtree 2. Then visit the root node 3. Visit all the nodes in the right subtree void inorder(struct node* root) { if (root == NULL) return; inorder(root->left); printf("%d ->", root->data); inorder(root->right); } 25
Preorder traversal 1. Visit root node 2. Visit all the nodes in the left subtree 3. Visit all the nodes in the right subtree void preorder(struct node* root) { if (root == NULL) return; printf("%d ->", root->data); preorder(root->left); preorder(root->right); } 26
Posteorder traversal 1. Visit all the nodes in the left subtree 2. Visit all the nodes in the right subtree 3. Visit the root node void postorder(struct node* root) { if (root == NULL) return; postorder(root->left); postorder(root->right); printf("%d ->", root->data); } 27
28
29
30
31
32
33
34
35
Inorder traversal (Using Stack) Inorder-Travesral-Algorithm 1. If(root==Null) Return; 1. While(true) If(root!=Null) Push(root); root=root. left; Else If(is. Empty. Stack) Break; Else root=Pop(); Print(root. data); root=root. Right; Time complexity O(n) Space complexity O(? ) 36
Inorder traversal (Using Stack) Inorder-Travesral-Algorithm 1. If(root==Null) Return; 1. While(true) If(root!=Null) Push(root); root=root. left; Else If(is. Empty. Stack) Break; Else root=Pop(); Print(root. data); root=root. Right; 37
Inorder traversal (Using Stack) Inorder-Travesral-Algorithm 1. If(root==Null) Return; 1. While(true) If(root!=Null) Push(root); root=root. left; Else If(is. Empty. Stack) Break; Else root=Pop(); Print(root. data); root=root. Right; 38
Inorder traversal (Using Stack) Inorder-Travesral-Algorithm 1. If(root==Null) Return; 1. While(true) If(root!=Null) Push(root); root=root. left; Else If(is. Empty. Stack) Break; Else root=Pop(); Print(root. data); root=root. Right; 39
Homework • Program Inorder, Preorder, and Postorder traversal using Stack. 40
Inorder traversal (Morris traversal: Without Stack and Recursion) Morris-Inorder-Travesral-Algorithm 1. Current=root 2. If(root==Null) Return; 3. While(Current!=Null) If(Current. left==Null) Visit(current) Current=Current. right Else Predecessor=Find. Predecessor(Cu rrent) If(! Predecessor. right) Predecessor. right=Current=Current. left Else Predecessor. right=Null Visit(Current) Current=Current. right Time complexity O(n) Space complexity O(? ) 41
Inorder traversal (Morris traversal: Without Stack and Recursion) Morris-Inorder-Travesral-Algorithm 1. Current=root 2. If(root==Null) Return; 3. While(Current!=Null) If(Current. left==Null) Visit(current) Current=Current. right Else Predecessor=Find. Predecessor(Cu rrent) If(! Predecessor. right) Predecessor. right=Current=Current. left Else Predecessor. right=Null Visit(Current) Current=Current. right Time complexity O(n) Space complexity O(1) 42
Inorder traversal (Morris traversal: Without Stack and Recursion) Morris-Inorder-Travesral-Algorithm 1. Current=root 2. If(root==Null) Return; 3. While(Current!=Null) rrent) t If(Current. left==Null) Visit(current) Current=Current. right Else Predecessor=Find. Predecessor(Cu If(! Predecessor. right) Predecessor. right=Curren Else Current=Current. left Predecessor. right=Null Visit(Current) Current=Current. right 43
Inorder traversal (Morris traversal: Without Stack and Recursion) Morris-Inorder-Travesral-Algorithm 1. Current=root 2. If(root==Null) Return; 3. While(Current!=Null) rrent) t If(Current. left==Null) Visit(current) Current=Current. right Else Predecessor=Find. Predecessor(Cu If(! Predecessor. right) Predecessor. right=Curren Else Current=Current. left Predecessor. right=Null Visit(Current) Current=Current. right 44
#include <stdio. h> #include<stdlib. h> void Morris(struct Node* root) { struct Node *curr, *Predecessor; struct Node { int data; struct Node* left_node; struct Node* right_node; }; struct Node* add_node(int data) { struct Node* node = malloc(sizeof(struct node)); node->data = data; node->left_node = NULL; node->right_node = NULL; } return (node); int main() { struct Node* root = add_node(1); root->left_node = add_node(2); root->right_node = add_node(3); root->left_node = add_node(4); root->left_node->right_node = add_node(5); root->left_node->right_node->left_node = add_node(6); Morris(root); return 0; } if (root == NULL) return; curr = root; while (curr != NULL) { if (curr->left_node == NULL) { printf("%d, ", curr->data); curr = curr->right_node; } else { /* Find the Predecessor of curr */ Predecessor = curr->left_node; while (Predecessor->right_node != NULL && Predecessor>right_node != curr) Predecessor = Predecessor->right_node; /* Make curr as the right child of its Predecessor */ if (Predecessor->right_node == NULL) { Predecessor->right_node = curr; curr = curr->left_node; } } /* fix the right child of Predecessor */ else { Predecessor->right_node = NULL; printf("%d, ", curr->data); curr = curr->right_node; } 45
#include <iostream> using namespace std; struct Node { int data; struct Node* left_node; struct Node* right_node; }; struct Node* add_node(int data) { struct Node* node = new Node; node->data = data; node->left_node = NULL; node->right_node = NULL; } return (node); int main() { struct Node* root = add_node(1); root->left_node = add_node(2); root->right_node = add_node(3); root->left_node = add_node(4); root->left_node->right_node = add_node(5); root->left_node->right_node->left_node = add_node(6); Morris(root); return 0; } void Morris(struct Node* root) { struct Node *curr, *Predecessor; if (root == NULL) return; curr = root; while (curr != NULL) { if (curr->left_node == NULL) { cout << curr->data << endl; curr = curr->right_node; } else { /* Find the Predecessor of curr */ Predecessor = curr->left_node; while (Predecessor->right_node != NULL && Predecessor>right_node != curr) Predecessor = Predecessor->right_node; /* Make curr as the right child of its Predecessor */ if (Predecessor->right_node == NULL) { Predecessor->right_node = curr; curr = curr->left_node; } /* Left subtree already visited , fix the right child of Predecessor */ else { Predecessor->right_node = NULL; cout << curr->data << endl; curr = curr->right_node; } } 46
Preorder traversal (Morris traversal: Without Stack and Recursion) How to modify the following pseudo code? Morris-Preorder-Travesral-Algorithm 1. Current=root 2. If(root==Null) Return; 3. While(Current!=Null) If(Current. left==Null) Visit(current) Current=Current. right Else Predecessor=Find. Predecessor(Cu rrent) If(! Predecessor. right) Predecessor. right=Current=Current. left Else Predecessor. right=Null Visit(Current) 47
Preorder traversal (Morris traversal: Without Stack and Recursion) How to modify the following pseudo code? Morris-Preorder-Travesral-Algorithm 1. Current=root 2. If(root==Null) Return; 3. While(Current!=Null) If(Current. left==Null) Visit(current) Current=Current. right Else Predecessor=Find. Predecessor(Cu rrent) If(! Predecessor. right) Predecessor. right=Current Visit(Current) Current=Current. left Else Predecessor. right=Null 48
Homework • Program Preorder and Postorder traversals using Morris method. 49
50
#include <stdio. h> #include<stdlib. h> struct node { int data; struct node* left; struct node* right; }; struct node* add_node(int data) { struct node* node = malloc(sizeof(struct node)); node->data = data; node->left_node = NULL; node->right_node = NULL; return (node); } int sum=0; int main() { struct Node* root = add_node(1); root->left_node = add_node(2); root->right_node = add_node(3); root->left_node = add_node(4); root->left_node->right_node = add_node(5); printf("The number of nodes is: %dn", numberofnodes(root) ); printf("The sum of values of all nodes: %dn", values(root)); return 0; } int numberofnodes(struct node* t) { if(t==NULL) return 0; return(1+numberofnodes(t->left)+numberofnodes(t->right)); } int values(struct node* t) { if(t==NULL) return 0; return(t->data+values(t->left)+values(t->right)); } 51
52
53
54
55
56
- Slides: 56