DSA and DAA

Download as pdf or txt
Download as pdf or txt
You are on page 1of 142

DATA STRUCTURES – Trees &

Hashing
UNIT-4&5
Dr. SELVA KUMAR S
ASSISTANT PROFESSOR
B.M.S. COLLEGE OF ENGINEERING
Trees - Introduction
• A tree is a nonlinear hierarchical data structure that
consists of nodes connected by edges.
• Different tree data structures allow quicker and
easier access to the data as it is a non-linear data
structure.
Properties of Trees
• There is one and only one path between every
pair of vertices in a tree.
• A tree with n vertices has n-1 edges.
• A graph is a tree if and if only if it is minimally
connected.
• Any connected graph with n vertices and n-1
edges is a tree.
Tree Applications
• Binary Search Trees(BSTs) are used to quickly
check whether an element is present in a set or
not.
• Heap is a kind of tree that is used for heap sort.
• A modified version of a tree called Tries is used in
modern routers to store routing information.
• Most popular databases use B-Trees and T-Trees,
which are variants of the tree structure we
learned above to store their data
• Compilers use a syntax tree to validate the syntax
of every program you write.
Tree terminologies
• Node - A node is an entity that contains a key or value and pointers to its
child nodes.
– The last nodes of each path are called leaf nodes or external
nodes that do not contain a link/pointer to child nodes.
– The node having at least a child node is called an internal node.
• Edge -It is the link between any two nodes.
• Root - It is the topmost node of a tree.
Tree terminologies
Root
• The first node from where the tree originates is called as a root node.
• In any tree, there must be only one root node.
• We can never have multiple root nodes in a tree data structure.
Edge
• The connecting link between any two nodes is called as
an edge.
• In a tree with n number of nodes, there are exactly (n-1)
number of edges.
Parent
• The node which has a branch from it to any other node is called as
a parent node.
• In other words, the node which has one or more children is called as a
parent node.
• In a tree, a parent node can have any number of child nodes.
Child
• The node which is a descendant of some node is called as
a child node.
• All the nodes except root node are child nodes.
Siblings
• Nodes which belong to the same parent are called as siblings.
• In other words, nodes with the same parent are sibling nodes.
Degree
• Degree of a node is the total number of children of that node.
• Degree of a tree is the highest degree of a node among all the
nodes in the tree.
Internal node
• The node which has at least one child is called as an internal
node.
• Internal nodes are also called as non-terminal nodes.
• Every non-leaf node is an internal node.
Leaf node
• The node which does not have any child is called as a leaf
node.
• Leaf nodes are also called as external nodes or terminal
nodes.
Level
• In a tree, each step from top to bottom is called as level of a
tree.
• The level count starts with 0 and increments by 1 at each level
or step.
Height
• Total number of edges that lies on the longest path from any leaf node to
a particular node is called as height of that node.
• Height of a tree is the height of root node.
• Height of all leaf nodes = 0
Depth
• Total number of edges from root node to a particular node is called as depth of that node.
• Depth of a tree is the total number of edges from root node to a leaf node in the longest
path.
• Depth of the root node = 0
• The terms “level” and “depth” are used interchangeably.
Subtree
• In a tree, each child from a node forms a subtree recursively.
• Every child node forms a subtree on its parent node.
Forest
• A forest is a set of disjoint trees.
Types of Tree
• General Tree
• Binary Tree
• Binary Search Tree
• AVL Tree
• Red-Black Tree
• N-ary Tree
Binary Tree
• Binary tree is a special tree data structure in which
each node can have at most 2 children.
• Thus, in a binary tree, Each node has either 0 child or
1 child or 2 children.
Unlabeled Binary Tree
• A binary tree is unlabeled if its nodes are not
assigned any label.
Example
• Consider we want to draw all the binary trees possible
• Number of binary trees possible with 3 unlabeled nodes
• = 2 x 3C3 / (3 + 1)
• = 6C3 / 4
• =5
Labeled Binary Tree
• A binary tree is labelled if all its nodes are
assigned a label.
Example
• Consider we want to draw all the binary trees possible with 3 labeled
nodes.
• Number of binary trees possible with 3 labeled nodes
• = { 2 x 3C3 / (3 + 1) } x 3!
• = { 6C3 / 4 } x 6
• =5x6
• = 30
Types of Binary Trees
Rooted Binary Tree
• A rooted binary tree is a binary tree that
satisfies the following 2 properties:
– It has a root node.
– Each node has at most 2 children.
Full/Strictly Binary Tree
• A binary tree in which every node has either 0 or 2
children is called as a Full binary tree.
• Full binary tree is also called as Strictly binary tree.
Complete /Perfect Binary Tree
• A complete binary tree is a binary tree that satisfies
the following 2 properties:
– Every internal node has exactly 2 children.
– All the leaf nodes are at the same level.
Almost Complete Binary Tree
• An almost complete binary tree is a binary tree that satisfies
the following 2 properties-
– All the levels are completely filled except possibly the last level.
– The last level must be strictly filled from left to right.
Skewed Binary Tree
• A skewed binary tree is a binary tree that satisfies the following 2 properties-
• All the nodes except one node has one and only one child.
• The remaining node has no child.
OR
• A skewed binary tree is a binary tree of n nodes such that its depth is (n-1).
Tree Traversal

• In order to perform any operation on a tree,


you need to reach to the specific node. The
tree traversal algorithm helps in visiting a
required node in the tree.
• Tree Traversal refers to the process of visiting
each node in a tree data structure exactly
once.
Tree traversal techniques

Tree Traversal

Depth First Traversal Breadth First Traversal

Preorder Traversal

Inorder Traversal

Postorder Traversal
Depth First Traversal
• Following three traversal techniques fall under
Depth First Traversal-
1. Preorder Traversal
2. Inorder Traversal
3. Postorder Traversal
Preorder Traversal
• Algorithm-
– Visit the root
– Traverse the left sub tree i.e. call Preorder (left sub tree)
– Traverse the right sub tree i.e. call Preorder (right sub tree)
Inorder Traversal
• Algorithm-
– Traverse the left sub tree i.e. call Inorder (left sub tree)
– Visit the root
– Traverse the right sub tree i.e. call Inorder (right sub tree)
Postorder Traversal
• Algorithm-
– Traverse the left sub tree i.e. call Postorder (left sub tree)
– Traverse the right sub tree i.e. call Postorder (right sub
tree)
– Visit the root
Breadth First Search
• Breadth First Traversal of a tree prints all the
nodes of a tree level by level.
• Breadth First Traversal is also called as Level
Order Traversal.
Binary Search Tree construction
• In a binary search tree (BST), each node contains:
– Only smaller values in its left sub tree
– Only larger values in its right sub tree
BST construction
• Number of distinct binary search trees possible with 3 distinct Nodes
= 2×3C3 / 3+1
= 6C3 / 4
=5
• If three distinct Nodes are A, B and C, then 5 distinct binary search trees
are:
Example
• Construct a Binary Search Tree (BST) for the following sequence of
numbers:
50, 70, 60, 20, 90, 10, 40, 100
• When elements are given in a sequence,
– Always consider the first element as the root node.
– Consider the given elements and insert them in the BST one by one.
Practice problem
• Problem-01:
• A binary search tree is generated by inserting in order of the following
integers-
50, 15, 62, 5, 20, 58, 91, 3, 8, 37, 60, 24
• The number of nodes in the left subtree and right subtree of the root
respectively is _____.
– (4, 7)
– (7, 4)
– (8, 3)
– (3, 8)
Practice problem
• Problem-02:
• How many distinct binary search trees can be constructed out
of 4 distinct keys?
– 5
– 14
– 24
– 35
C code – BST construction
BST Traversal

• Inorder traversal of a binary search tree always yields all the nodes in increasing order.
C Code – BST Traversal
Void inorder(node *root)
{
if(root!=NULL)
{
inorder(root->left);
printf("%d ",root->data);
inorder(root->right);
}
}
C Code – BST Traversal
void postorder(node *root)
{
if(root!=NULL)
{
postorder(root->left);
postorder(root->right);
printf("%d ",root->data);
}
}
C Code – BST Traversal
void preorder(node *root)
{
if(root!=NULL)
{
printf("%d ",root->data);
preorder(root->left);
preorder(root->right);
}
}
Practice
• Suppose the numbers 7 , 5 , 1 , 8 , 3 , 6 , 0 , 9 , 4 ,
2 are inserted in that order into an initially empty
binary search tree. The binary search tree uses
the usual ordering on natural numbers.
• What is the inorder traversal sequence of the
resultant tree?
A. 7,5,1,0,3,2,4,6,8,9
B. 0,2,4,3,1,6,5,9,8,7
C. 0,1,2,3,4,5,6,7,8,9
D. 9,8,6,4,2,3,0,1,5,7
Problem-2
• The preorder traversal sequence of a binary
search tree is-
– 30 , 20 , 10 , 15 , 25 , 23 , 39 , 35 , 42
• Which one of the following is the postorder
traversal sequence of the same tree?
A. 10 , 20 , 15 , 23 , 25 , 35 , 42 , 39 , 30
B. 15 , 10 , 25 , 23 , 20 , 42 , 35 , 39 , 30
C. 15 , 20 , 10 , 23 , 25 , 42 , 35 , 39 , 30
D. 15 , 10 , 23 , 25 , 20 , 35 , 42 , 39 , 30
BST Operations
• Commonly performed binary search tree
operations are:
BST Operations

Search Operation Insertion Operation Deletion Operation


Search Operation
• Search Operation is performed to search a
particular element in the Binary Search Tree.
• For searching a given key in the BST,
– Compare the key with the value of root node.
– If the key is present at the root node, then return the
root node.
– If the key is greater than the root node value, then
recur for the root node’s right subtree.
– If the key is smaller than the root node value, then
recur for the root node’s left subtree.
C Code – BST Search Operation
struct node *search(struct node *node, int key) {
// Return NULL if the tree is empty
if (node == NULL) return NULL;
if (node->key == key) return node->data;
if (key < node->key)
search(node->left, key);
else
search(node->right, key);
}
Insertion operation
• The insertion of a new key always takes place
as the child of some leaf node.
• For finding out the suitable leaf node,
– Search the key to be inserted from the root node
till some leaf node is reached.
– Once a leaf node is reached, insert the key as child
of that leaf node.
C Code – Insertion Operation
// Create a node
struct node *newNode(int item) {
struct node *temp = (struct node *)malloc(sizeof(struct node));
temp->key = item;
temp->left = temp->right = NULL;
return temp;
}
// Insert a node
struct node *insert(struct node *node, int key) {
// Return a new node if the tree is empty
if (node == NULL) return newNode(key);

// Traverse to the right place and insert the node


if (key < node->key)
node->left = insert(node->left, key);
else
node->right = insert(node->right, key);

return node;
}
Deletion Operation
• Deletion Operation is performed to delete a
particular element from the Binary Search
Tree.
• When it comes to deleting a node from the
binary search tree, three cases are possible.
Case-01: Deletion Of A Node Having
No Child (Leaf Node)
• Just remove / disconnect the leaf node that is
to deleted from the tree.
Case-02: Deletion Of A Node Having
Only One Child
• Consider the following example where node
with value = 30 is deleted from the BST.
Case-03: Deletion Of A Node Having
Two Children
• Consider the following example where node with
value = 15 is deleted from the BST
• Method-1:
– Visit to the right subtree of the deleting node.
– Pluck the least value element called as inorder successor.
– Replace the deleting element with its inorder successor.
• Method-2:
– Visit to the left subtree of the deleting node.
– Pluck the greatest value element called as inorder successor.
– Replace the deleting element with its inorder successor.
C code – BST deletion
//Function to find minimum in a tree.
Node* FindMin(Node* root)
{
while(root->left != NULL) root = root->left;
return root;
}

// Function to search a delete a value from tree.


struct Node* Delete(struct Node *root, int data) {
if(root == NULL) return root;
else if(data < root->data) root->left = Delete(root->left,data);
else if (data > root->data) root->right = Delete(root->right,data);
else {
// Case 1: No child
if(root->left == NULL && root->right == NULL) {
free(root);
root = NULL;
}
//Case 2: One child
else if(root->left == NULL) {
struct Node *temp = root;
root = root->right;
free(temp);
}
else if(root->right == NULL) {
struct Node *temp = root;
root = root->left;
free(temp);
}
// case 3: 2 children
else {
struct Node *temp = FindMin(root->right);
root->data = temp->data;
root->right = Delete(root->right,temp->data);
}
}
return root;
}
Hashing
• Hashing is a well-known technique to search any
particular element among several elements.
• It minimizes the number of comparisons while
performing the search.
• Unlike other searching techniques,
– Hashing is extremely efficient.
– The time taken by it to perform the search does not
depend upon the total number of elements.
– It completes the search with constant time complexity
O(1).
Hashing Mechanism
• An array data structure called as Hash table is
used to store the data items.
• Based on the hash key value, data items are
inserted into the hash table.
Hash Key value
• Hash key value is a special value that serves as an index for a
data item.
• It indicates where the data item should be stored in the hash
table.
• Hash key value is generated using a hash function.
Hash Function
• Hash function is a function that maps any big
number or string to a small integer value.
• Hash function takes the data item as an input
and returns a small integer value as an output.
• The small integer value is called as a hash
value.
• Hash value of the data item is then used as an
index for storing it into the hash table.
Types of Hash Functions
• There are various types of hash functions
available such as-
• Mid Square Hash Function
• Division Hash Function
• Folding Hash Function etc

• It depends on the user which hash function


wants to use.
Properties of Hash Function
• The properties of a good hash function are-
– It is efficiently computable.
– It minimizes the number of collisions.
– It distributes the keys uniformly over the table.
Collision in Hashing
• Hash function is used to compute the hash value for
a key.
• Hash value is then used as an index to store the key
in the hash table.
• Hash function may return the same hash value for
two or more keys.
• When the hash value of a key maps to an already
occupied bucket of the hash table, it is called as
a Collision.
Collision Resolution Techniques
• Collision Resolution Techniques are the techniques
used for resolving or handling the collision.
Collision Resolution
Techniques

Separate Chaining Open Addressing


(Open Hashing) (Closed Hashing)

Linear Probing

Quadratic Probing

Double Hashing
Separate Chaining
• To handle the collision,
– This technique creates a linked list to the slot for
which collision occurs.
– The new key is then inserted in the linked list.
– These linked lists to the slots appear like chains.
– That is why, this technique is called as separate
chaining.
Example-Separate Chaining
• Using the hash function ‘key mod 7’, insert the
following sequence of keys in the hash table-
• 50, 700, 76, 85, 92, 73 and 101
Step-1
• Draw an empty hash table.
• For the given hash function, the possible range of hash values
is [0, 6].
• So, draw an empty hash table consisting of 7 buckets as-
Step-2
• Insert the given keys in the hash table one by one.
• The first key to be inserted in the hash table = 50.
• Bucket of the hash table to which key 50 maps = 50 mod 7 = 1.
• So, key 50 will be inserted in bucket-1 of the hash table as-
Step-3
• The next key to be inserted in the hash table = 700.
• Bucket of the hash table to which key 700 maps = 700 mod 7 = 0.
• So, key 700 will be inserted in bucket-0 of the hash table as-
Step-4
• The next key to be inserted in the hash table = 76.
• Bucket of the hash table to which key 76 maps = 76 mod 7 = 6.
• So, key 76 will be inserted in bucket-6 of the hash table as-
Step-5
• The next key to be inserted in the hash table = 85.
• Bucket of the hash table to which key 85 maps = 85 mod 7 = 1.
• Since bucket-1 is already occupied, so collision occurs.
• Separate chaining handles the collision by creating a linked list to bucket-1.
• So, key 85 will be inserted in bucket-1 of the hash table as-
Step-6
• The next key to be inserted in the hash table = 92.
• Bucket of the hash table to which key 92 maps = 92 mod 7 = 1.
• Since bucket-1 is already occupied, so collision occurs.
• Separate chaining handles the collision by creating a linked list to bucket-1.
• So, key 92 will be inserted in bucket-1 of the hash table as-
Step-7
• The next key to be inserted in the hash table = 101.
• Bucket of the hash table to which key 101 maps = 101 mod 7 = 3.
• Since bucket-3 is already occupied, so collision occurs.
• Separate chaining handles the collision by creating a linked list to bucket-3.
• So, key 101 will be inserted in bucket-3 of the hash table as-
Open Addressing
• In open addressing,
– Unlike separate chaining, all the keys are stored
inside the hash table.
– No key is stored outside the hash table.

• Techniques used for open addressing are-


– Linear Probing
– Quadratic Probing
– Double Hashing
Operations in Open Addressing
• Insert Operation:
– Hash function is used to compute the hash value for a
key to be inserted.
– Hash value is then used as an index to store the key in
the hash table.

• In case of collision,
– Probing is performed until an empty bucket is found.
– Once an empty bucket is found, the key is inserted.
– Probing is performed in accordance with the
technique used for open addressing.
Open Addressing
• Search Operation:
• To search any particular key,
– Its hash value is obtained using the hash function
used.
– Using the hash value, that bucket of the hash table is
checked.
– If the required key is found, the key is searched.
– Otherwise, the subsequent buckets are checked until
the required key or an empty bucket is found.
– The empty bucket indicates that the key is not present
in the hash table.
Open Addressing
• Delete Operation:
– The key is first searched and then deleted.
– After deleting the key, that particular bucket is
marked as “deleted”.
1. Linear Probing
• In linear probing,
– When collision occurs, we linearly probe for the next bucket.
– We keep probing until an empty bucket is found.

• Advantage-

– It is easy to compute.

• Disadvantage-

– The main problem with linear probing is clustering.


– Many consecutive elements form groups.
– Then, it takes time to search an element or to find an empty bucket.
Linear Probing
Example- Linear Probing
2. Quadratic Probing
• In quadratic probing,
• When collision occurs, we probe for i2‘th
bucket in ith iteration.
• We keep probing until an empty bucket is
found.
Quadratic Probing
Example – Quadratic Probing
Double Hashing
• In double hashing,
• We use another hash function hash2(x) and
look for i * hash2(x) bucket in ith iteration.
• It requires more computation time as two
hash functions need to be computed.
Double Hashing
Example –Double Hashing
Separate chaining Vs Open Addressing
Comparison of Open Addressing
Techniques
Example
Rehashing
• Hash tables offer exceptional performance when
not overly full.
• This is the traditional dilemma of all array-based
data structures:
• Make the table too small, performance degrades
and the table may overflow
• Make the table too big, and memory gets wasted.
• Rehashing or variable hashing attempts to
circumvent this dilemma by expanding the hash
table size whenever it gets too full.
Load Factor and Rehashing
• Load factor λ (lambda) is the ratio of the number
of elements to the size of the hash table λ = n/N
where n denotes the number of elements and N
the number of locations in the hash table
• For the open addressing scheme, λ is between 0
and 1
• If the hash table is empty, then λ = 0
• If the hash table is full, then λ = 1
• For the separate chaining scheme, λ can be any
value.
How is the Load Factor Used?
• The Load Factor decides “when to increase the size of the hash Table.”
• The load factor can be decided using the following formula:
Initial capacity of the HashTable * Load factor of the HashTable

• E.g. If The initial capacity of HashTable is = 16


and the load factor of HashTable = 0.75

• According to the formula as mentioned above: 16 * 0.75 = 12

• It represents that the 12th key-value pair of a HashTable will keep its
size to 16.
• As soon as the 13th element (key-value pair) will come into the
HashTable, it will increase its size from 16 buckets to 16 * 2 = 32
buckets.
Rehashing Example
• Say we had HashTable with Initial Capacity of 4.
• We need to insert 4 Keys: 100, 101, 102, 103
• And say the Hash function used was division method: Key %
ArraySize

• So Hash(100) = 1, so Element2 stored at 1st Index.


• So Hash(101) = 2, so Element3 stored at 2nd Index.
• So Hash(102) = 0, so Element1 stored at 3rd Index.

• With the insertion of 3 elements, the load on Hash Table = ¾ = 0.74

• So we can add this 4th element to this Hash table, and we need to
increase its size to 6 now.
Example continued..
• E.g. The earlier hash function was Key%3 and
now it is Key%6.
• So Hash(100) = 4, so Element stored at 4th Index.
• So Hash(101) = 5, so Element stored at 5th Index.
• So Hash(102) = 0, so Element stored at 0th Index.
• So Hash(103) = 1, so Element stored at 1st Index.
Example continued..
• Element1: Hash(100) = 100%6 = 4, so Element1 will be rehashed and will be stored
at 5th Index in this newly resized HashTable, instead of 1st Index as on previous
HashTable.

• Element2: Hash(101) = 101%6 = 5, so Element2 will be rehashed and will be stored


at 6th Index in this newly resized HashTable, instead of 2nd Index as on previous
HashTable.

• Element3: Hash(102) = 102%6 = 6, so Element3 will be rehashed and will be stored


at 4th Index in this newly resized HashTable, instead of 3rd Index as on previous
HashTable.

• Since the Load Balance now is 3/6 = 0.5, we can still insert the 4th element now.

• Element4: Hash(103) = 103%6 = 1, so Element4 will be stored at 1st Index in this


newly resized HashTable.
Rehashing Steps
• For each addition of a new entry to the map, check the current load factor.
• If it’s greater than its pre-defined value, then Rehash.
• For Rehash, make a new array of double the previous size and make it the new
bucket array.
• Then traverse to each element in the old bucketArray and insert them back so
as to insert it into the new larger bucket array.
Extendible hashing
• Extendible hashing is a dynamic approach to managing
data. In this hashing method, flexibility is a crucial
factor. This method caters to flexibility so that even the
hashing function dynamically changes according to the
situation and data type.

• Directories and buckets are two


key terms in this algorithm.
• Buckets are the holders of hashed
data, while directories are the
holders of pointers pointing
towards these buckets.
• Each directory has a unique ID.
Algorithm
1.Initialize the bucket depths and the global depth of the directories.
2.Convert data into a binary representation.
3.Consider the "global depth" number of the least significant bits
(LSBs) of data.
4.Map the data according to the ID of a directory.
5.Check for the following conditions if a bucket overflows (if the
number of elements in a bucket exceeds the set limit):
1.Global depth == bucket depth: Split the bucket into two and increment
the global depth and the buckets' depth. Re-hash the elements that were
present in the split bucket.
2.Global depth > bucket depth: Split the bucket into two and increment the
bucket depth only. Re-hash the elements that were present in the split
bucket.
6.Repeat the steps above for each element.
Example
• Let's take the following example to see how this hashing method works where:
• Data = {28,4,19,1,22,16,12,0,5,7}
• Bucket limit = 3

• Convert the data into binary representation:


• 28 = 11100
• 4 = 00100
• 19 = 10011
• 1 = 00001
• 22 = 10110
• 16 = 10000
• 12 = 01100
• 0 = 00000
• 5 = 00101
• 7 = 00111
Assignment
• Write the solution for the below given data
applying Extendible Hashing Technique.
• 16, 4,6, 22, 24,10, 31, 7, 9, 20, 26
• Bucket size assumed to be 3.
Solution
• First convert to binary format:
• 16: 100000
• 4: 00100
• 6: 00110
• 22:10110
• 24:11000
• 10:01010
• 31:11111
• 7:00111
• 9:01001
• 20:10100
• 26:11010
• Initially global depth = local depth = 1
• Insert 16 , 4 , 6
• Insert 22
• Overflow occurs since bucket is full.
• Here, local depth = global depth
• So, Do the Directory expansion & bucket splitting
• Insert 24 and Insert 10

• Insert 31, 7 and 9


• Insert 20
• Overflow condition occurs
• Local depth = Global depth
• So, Directory expansion & bucket splitting
• Insert 26
• Overflow condition occurs
• Local depth < Global depth
• So, Only bucket splitting occurs
Questions
• implementation to find leaf count of a given
Binary tree
int getLeafCount(Node node)
{
if (node == null)
return 0;
if (node.left == null && node.right == null)
return 1;
else
return getLeafCount(node.left) +
getLeafCount(node.right);
}
• counting the number of nodes in a Tree
int countnodes(struct node *root)
{
if(root != NULL)
{
countnodes(root->left);
count++;
countnodes(root->right);
}
return count;
}
• // Function to find k'th smallest element in BST
• // Here i denotes the number of nodes processed so far
• int kthSmallest(Node* root, int *i, int k)
• {
• // base case
• if (root == nullptr)
• return INT_MAX;

• // search in left subtree
• int left = kthSmallest(root->left, i, k);

• // if k'th smallest is found in left subtree, return it
• if (left != INT_MAX)
• return left;

• // if current element is k'th smallest, return its value
• if (++*i == k)
• return root->data;

• // else search in right subtree
• return kthSmallest(root->right, i, k);
• }

• // Function to find k'th smallest element in BST
• int kthSmallest(Node* root, int k)
• {
• // maintain index to count number of nodes processed so far
• int i = 0;

• // traverse the tree in in-order fashion and return k'th element
• return kthSmallest(root, &i, k);
• }
// Function to determine if given Binary Tree is a BST or not by keeping a
// valid range (starting from [MIN_VALUE, MAX_VALUE]) and keep shrinking
// it down for each node as we go down recursively
bool isBST(Node* node, int minKey, int maxKey)
{
// base case
if (node == NULL)
return true;

// if node's value fall outside valid range


if (node->data < minKey || node->data > maxKey)
return false;

// recursively check left and right subtrees with updated range


return isBST(node->left, minKey, node->data) &&
isBST(node->right, node->data, maxKey);
}
• int maxDepth(struct node* node)
• {
• if (node==NULL)
• return 0;
• else
• {
• /* compute the depth of each subtree */
• int lDepth = maxDepth(node->left);
• int rDepth = maxDepth(node->right);

• /* use the larger one */
• if (lDepth > rDepth)
• return(lDepth+1);
• else return(rDepth+1);
• }
• }

You might also like