Chapter 6: Trees Part III

Chapter 6: Trees Part III

11. Binary Search Trees (BSTs)

As the name suggests, the main use of this representation is for searching which is implemented by imposing restriction on the kind of data a node can contain. All left subtree elements should be less than root data and all the right subtree elements should be greater than root data. As a result, it reduce the worst case average search operation to O(logn).

Note

  • Since root data is always between left subtree data and right subtree data, performing InOrder traversal on BST produces a sorted list.
  • If the left subtree root data is less than the element we want to search, then skip it. The BST consider either left or right subtrees for searching an element but not both.
  • While performing basic operation such as insertion, deletion, searching on BST, the height of the tree gets changed each time. Hence there exists variations in time complexities of best case, average case and worst case.
  • The basic operation on a BST take time proportional to the height of the tree.

Operations

Find an Element in BSTs

Recursive version

struct BinarySearchTreeNode *Find(struct BinarySearchTreeNode *root, int data){
	if(root==NULL) return NULL;
	if(data<root->data) return Find(root->left, data);
	else if(data>root->data) return Find(root->right, data);
	return root;
}
  • Time Complexity: O(n), in worst case when BST is a skew tree. Space Complexity: O(n) for recursive stack.

Non-recursive version

struct BinarySearchTreeNode *Find(struct BinarySearchTreeNode *root, int data){
	if(root==NULL) return NULL;
	while(root){
		if(data==root->data)
			return root;
		else if(data>root->data)			
			root=root->right;					//either go right
		else 
			root=root->left;					//or go left
	}
	return NULL;
}	
  • Time Complexity: O(n), Space Complexity: O(1).
Find Minimum / Maximum Element in BSTs

Find the left most or right most node.
Recursive version

struct BinarySearchTreeNode *FindMin(struct BinarySearchTreeNode *root){
	if(root==NULL)
		return NULL;
	else if(root->left==NULL)
		return root;
	else
		return FindMin(root->left);
}

Non-recursive version

struct BinarySearchTreeNode *FindMin(struct BinarySearchTreeNode *root){
	if(root==NULL)
		return NULL;
	while(root->left)
		root=root->left;
	return root;
}
Where is InOrder Predecessor and Successor
  • If X has two children then its inorder predecessor is the maximum value in its left subtree (rightmost in left subtree) and its inorder successor is the minimum value in its right subtree (left most in right subtree).
    在这里插入图片描述
  • If X does not have a left child, then its inorder predecessor is its first left ancestor. If X does not have a right child, then its inorder successor is its first right ancestor.
    在这里插入图片描述
Insert an Element in BSTs
struct BinarySearchTreeNode *Insert(struct BinarySearchTreeNode *root, int data){
	if(!root){
		root=struct BinarySearchTreeNode *)malloc(sizeof(struct BinarySearchTreeNode));
		if(!root) return;						//memory error
		else{
			root->data=data;
			root->left=root->right=NULL;
		}
	}
	else{
		if(data<root->data)
			root->left=Insert(root->left, data);
		else if(data>root->data)
			root->right=Insert(root->right, data);
	}
	return root;
}	
Delete an Element in BSTs
  • If the element to be deleted is a leaf node, return NULL to its parent which means make the corresponding child pointer NULL.
  • If the element to be deleted has one child, send the current node’s child to its parent.
  • If the element to be deleted has two children, the general strategy is to replace the key of this node with the largest element of the left subtree and recursively delete that node. The largest node in he left subtree cannot have a right child, so the second delete is an easy one. Also we can replace with minimum element in the right subtree.
struct BinarySearchTreeNode *Delete(struct BinarySearchTreeNode *root, int data){
	struct BinarySearchTreeNode *temp;
	if(root==NULL)
		printf("Element not exists");
	else if(data<root->data)
		root->left=Delete(root->left, data);
	else if(data>root->data)
		root->right=Delete(root->right, data);
	else{															//already found element
		if(root->left && root->right){								//two children
			/*replace with largest in left subtree*/
			temp=FindMax(root->left);
			root->data=temp->data;
			root->left=Delete(root->left, root->data);				//recursive, transform into one child case
		}
		else{														//one child
			temp=root;
			if(root->left==NULL)
				root=root->right;
			if(root->right==NULL)
				root=root->left;
			free(temp);
		}
	}
	return root;
}	

12. Binary Search Tree: Problems & Solutions

Pro 1.1: Find the Lowest Common Ancestor (LCA)

Given two pointers to two nodes in a binary search tree, find the lowest common ancestor.

Solution :

  • While traversing BST in PreOrder from root to bottom, the first node we encounter with value between α \alpha α and β \beta β is the Least Common Ancestor of α \alpha α and β \beta β.
  • While Inorder traversal has advantages over others as it gives the sorted order, we can also consider preorder traversal in the LCA finding problem.
struct BinarySearchTreeNode *FindLCA(struct BinarySearchTreeNode *root, struct BinarySearchTreeNode *a, struct BinarySearchTreeNode *b)){
	while(root){
		if((a->data<root->data && b->data>root->data)||(a->data>root->data && b->data<root->data))
			return root;
		if(a->data<root->data && b->data<root->data)				//textbook only judge whether a<root ?
			root=toot->left;
		else if(a->data>root->data && b->data>root->data)			//textbook only use 'else'
			root=root->right;
	}
}	 

Pro 1.2: Find the shortest path between two nodes in BST

Solution : find LCA

Pro 2: Check Whether the Tree is a BST

Solution 1: Wrong algorithm X

int IsBST(struct BinaryTreeNode *root){
	if(root==NULL) 
		return 1;
	if(root->left && root->left->data>root->data)
		return 0;
	if(root->right && root->right->data<root->data)
		return 0;
	if(!IsBST(root->left) || !IsBST(root->right))		//recursively check the left or right is not a BST 
		retrun 0;			
	return 1;											//pass all
} 
	6						this tree will pass the above code 
   / \						while it is no a BST 
  2   8						Check only at a current node's left and right is not enough.
 / \
1   9

Solution 2: Correct Algorithm

  • For each node, check if max value in left subtree is smaller than the current node data and min value in right subtree greater than the node data.
int IsBST(struct BinaryTreeNode *root){
	if(root==NULL) 
		return 1;
	if(root->left && FindMax(root->left)>root->data)
		return 0;
	if(root->right && FindMin(root->right)<root->data)
		return 0;
	if(!IsBST(root->left) || !IsBST(root->right))		//recursively check the left or right is not a BST 
		retrun 0;			
	return 1;											//pass all
} 
  • Time Complexity: O(n2), Space Complexity: O(n).

Solution 3: improve complexity

  • A better solution is to look at each node only once. The trick is to write a utility helper function that traverses down the tree keeping track of the narrowing min and max allowed values as it goes.
IsBST(root, INT_MIN, INT_MAX);					//initial call from intmax and intmin where they narrow from
int ISBST(struct BinaryTreeNode *root, int min, int max){
	if(!root) return 1;
	return (root->data>min && root->data<max && IsBST(root->left, min, root->data) && IsBST(root->right, root->data, max));
  • Time Complexity: O(n), Space Complexity: O(n).

Solution 4: further improve the complexity

  • The idea behind the solution is that inorder traversal of BST produces sorted lists.
int prev=INT_MIN;										//initialize 
int IsBST(struct BinaryTreeNode *root, int *prev){
	if(!root) return 1;
	if(!IsBST(root->left, prev)) return 0;
	if(root->data<*prev) return 0;
	*prev=root->data;
	return IsBST(root->right, prev);
}
  • Time Complexity: O(n), Space Complexity: O(n).

*Pro 3.1: Convert BST to Circular DLL

With space Complexity O(1).

Solution 1:

  • Convert left and right subtrees to DLLs and maintain end of those lists. Then adjust the pointers.
struct BinarySearchTreeNode *BST2DLL(struct BinarySearchTreeNode *root, struct BinarySearchTreeNode **Ltail){
	struct BinarySearchTreeNode *left, *ltail, *right, *rtail;
	if(!root){
		*ltail=NULL;
		return NULL;
	}
	left=BST2DLL(root->left, &ltail);
	right=BST2DLL(root->right, &rtail);
	root->left=ltail
	root->right=right;
	if(!right)
		*ltail=root;
	else{
		right->left=root;
		*ltail=rtail;
	}
	if(!left)
		return root;
	else{
		ltail->right=root;
		return left;
	}
}		

Solution 2: Divide and Conquer

struct BinarySearchTreeNode *Append(struct BinarySearchTreeNode *a, struct BinarySearchTreeNode *b){
	struct BinarySearchTreeNode *aLast, *bLast;
	if(a==NULL)
		return b;
	if(b==NULL)
		return a;
	aLast=a->left, bLast=b->left;
	aLast->right=b;
	b->left=aLast;
	bLast->right=a;
	a->left=bLast;
	return a;
}
struct BinarySearchTreeNode* BST2DLL(struct BinarySearchTreeNode *root){
	struct BinarySearchTreeNode *aList, *bList;
	if(root==NULL)
		return NULL;
	aList=BST2DLL(root->left);
	bList=BST3DLL(root->right);
	root->left=root;
	root->right=root;
	aList=Append(aList, root);
	aList=Appenda(List, bList);
	return aList;
}	

*Pro 3.2: Convert DLL to BST

Given a sorted doubly linked list, give an algorithm for converting it into balanced binary search tree.

Solution : Find the middle node and adjust the pointers

struct DLL2BalancedBST(struct DLLNode *head){
	struct DLLNode *temp, *p, *q;
	if(!head || !head->next)
		return head;
	temp=FindMiddleNode(head);
	p=head;
	while(p->next!=temp)
		p=p->next;
	p->next=NULL;
	q=temp->next;
	temp->next=NULL;
	temp->prev=DLL2BalancedBST(head);
	temp->next=DLL2BalancedBST(q);
	return temp;
}
  • Time Complexity: O(nlogn) for find the middle node.

Pro 3.3: Convert Array to BST

Given a sorted array, give an algorithm for converting the array to BST.

Solution : Divide and Conquer

struct BinaryTreeNode *BuildBST(int a[], int left, int right){
	int mid;
	if(left>right) return NULL;
	struct BinaryTreeNode *newNode=(struct BinaryTreeNode *)malloc(sizeof(struct BinaryTreeNode));
	if(!newNode) return ; 		//memory error
	if(left==right){
		newNode->data=a[left];
		newNode->left=newNode->right=NULL;
	}
	else{
		mid=left+(right-left)/2;
		newNode->data=a[mid];
		newNode->left=BuildBST(a, left, mid-1);
		newNode->right=BuildBST(a, mid+1, right);
	}
	return newNode;
}
  • Time Complexity: O(n), Space Complexity: O(n).

Pro 3.4: Convert SLL to BST

Given a singly linked list where elements are sorted in ascending order, convert it to a height balanced BST.

Solution 1: similar as pro 3.2

  • In each recursive call we would have to traverse half of the list length to find the middle element. The run time complexity is O(nlogn). This is because each level of recursive call requires a total of n/2 traversal steps in the list and there are a total of logn number of level (for balance tree).

Solution 2: Bottom-Up Approach

  • The bottom-up approach enables us to access the list in its order while creating node and then assign their parent.
struct BinaryTreeNode *SLL2BST(struct ListNode *& list, int start, int end){
	if(start>end) return NULL;
	int mid=start+(end-start)/2;
	struct BinaryTreeNode *leftChild=SLL2BST(list, start, mid-1);
	struct BinaryTreeNode *parent=(struct BinaryTreeNode *)malloc(sizeof(struct BinaryTreeNode));
	if(!parent) return ; //memory error
	parent->data=list->data;
	parent->left=leftChild;
	list=list->next;
	parent->right=SLL2BST(list, mid+1, end);
	return parent;
}	 	

Pro 4: Find kth Smallest Element

Solution :

  • While inorder traversing the BST, keep track of the number of elements visited.
struct BinarySearchTreeNode *kthSmallest(struct BinarySearchTreeNode *root, int k, int *count){
	if(!root) return NULL;
	struct BinarySearchTreeNode *temp=kthSmallest(root->left, k, count);		//go deep to the leftmost
	if(temp) return temp;
	if(++count==k) return root;													//first increse count then compare
	return kthSmallest(root->right, k, count);
}

Pro 5: Floor and Ceiling

If a given key is less than the key at the root of a BST then the floor of the key (the largest key in BST less than or equal to the key) must be in the left subtree.

If the key is greater than the key at the root, then the floor if the key could be in the right subtree, but only if there is a key smaller than or equal to the key in the right subtree; if not (or the key is equal to the key at the root) then the key at the root is the floor of the key.

Finding the ceiling is similar, with interchanging right and left.

Solution : Inorder Traversal

  • While traversing the BST in inorder, keep track of the values being visited.
struct BinaryTreeNode *Floor(struct BinaryTreeNode *root, int data){
	struct BinaryTreeNode *prev=NULL;
	return FloorUtil(root, prev, data);
}
struct BinaryTreeNode *FloorUtil(struct BinaryTreeNode *root, struct BinaryTreeNode *prev, int data){
	if(!root) return NULL;
	if(!FloorUtil(root->left, prev, data)) return 0;		//go left tree, case1:find exit; case2:no node return 0
	if(root->data==data) return root;
	if(root->data>data) return prev;
	prev=root;
	return FloorUtil(root->right, prev, data);				//go right tree,
}
struct BinaryTreeNode *Ceiling(struct BinaryTreeNode *root, int data){
	struct BinaryTreeNode *prev=NULL;
	return CeilingUtil(root, prev, data);
}
struct BinaryTreeNode *CeilingUtil(struct BinaryTreeNode *root, struct BinaryTreeNode *prev, int data){
	if(!root) return NULL;
	if(!CeilingUtil(root->right, prev, data)) return 0;
	if(root->data==data) return root;
	if(root->data<data) return prev;
	prev=root;
	return CeilingUtil(root->left, prev, data);
}
  • Note: A good recursive version to go right subtree and go left subtree. Hard to comprehend but need draw the draft.

Pro 6: Find the Union and Intersection of BSTs

Solution 1: Assume parent pointers are available (say threaded binary trees)

  • If parent pointers are available then the problem is same as merging of two sorted lists. This is because if we call inorder successor each time we get next highest element. It transforms into a matter of which inorder successor to call.
  • Time Complexity: O(m+n), Space Complexity: O(1).

Solution 2: Assume parent pointers are not available

  • Convert them to linked lists and then merge.

Solution 3: Inorder traversal

  • Perform inorder traversal on one of the BSTs. While performing the traversal store them in hash table. After completion of the traversal of first BST, start traversal of second BST and compare them with hash table contents.
  • Time Complexity: O(m+n), Space Complexity: O(Max(m,n))

Pro 7: Trim the Tree

Given a BST and two numbers k1 and k2, give an algorithm for printing all the elements of BST in the range k1 and k2.

Solution 1: recursion

void RangeTrim(struct BinarySearchTreeNode *root, int k1, int k2){
	if(root==NULL) return ;
	if(root->data>=k1) 
		RangeTrim(root->left, k1, k2);
	if(root->data>=k1 && root->data<=k2) 
		printf("%d",root->data);
	if(root->data<=k2)
		RangePrinter(root->right, k1, k2);
}
/*note that the 3 if order is important which ensure that print a sorted list*/	

Solution 2: level order traversal

  • While adding the elements to queue check for the range.
void RangeTrim(struct BinarySearchTreeNode *root, int k1, int k2){
	struct BinarySearchTreeNode *temp;
	struct Queue *q=CreateQueue();
	if(!root) return NULL;
	q.EnQueue(q, root);
	while(!IsEmptyQueue(q)){
		temp=DeQueue(q);
		if(temp->data>=k1 && temp->data<=k2)
			printf("%d", temp->data);
		if(temp->left && temp->data>=k1)
			EnQueue(Q, temp->left);
		if(temp->right && temp->data<=k2)
			EnQueue(Q, temp->right);
	}
	DeleteQueue(q);
	return NULL;
}	

Solution 3:

  • First locate k1 with normal binary search and after that use inorder successor until we encounter k2.

Pro 8: Check Whether two BSTs are the Same

eg: Two BSTs with data [10, 5, 20, 15, 30] and [10, 15, 30, 20, 5] should return false.

Solution 1:

  • Performing an inorder traversal on first tree and storing its data in hash table. Then perform in order traversal on second tree and check whether that data is already in hash table or not (if it exists in hash table then mark it with -1 or some unique value). During the traversal of second tree if we find any mismatch return false. After traversal of second tree check whether it has all -1s in the hash table or not (this ensures extra data available in second tree).
  • Time Complexity: O(max(m,n)), Space Complexity: O(max(m,n))

Solution 2: reduce complexity

  • Perform inorder traversal of both the trees in parallel and check whether both the trees are generating the same element at the same position.
  • Time Complexity: O(max(m,n)), Space Complexity: O(1)

Pro 9: How Many Structurally Unique BSTs

For the key value 1…n, how many structurally unique BSTs are possible that store those keys.

Solution : Dynamic Programming

  • Consider that each value could be the root, recursively find the size of the left and right subtrees.
int CountTrees(int n){
	if(n<=1) return 1;
	else{
		int sum=0;
		int left, right, root;
		for(root=1; root<=n; root++){
			left=CountTrees(root-1);
			right=CountTrees(n-root);
			sum+=lef*right;					
		}
		return sum;
	}
}	
/* Sum(n)=f(1)+f(2)+...+f(n)   f(i)=Sum(i-1)*Sum(n-i) */
/* For more, refer to 'Catalan Number' */

Pro 10: Find the Number of Keys greater than k

Give a BST of size n, in which each node r has an additional field (r–size), the number of the key in the subtree rooted at r (including the root node r). Give an O(h) algorithm to find the number of keys that are strictly greater than k.

Solution :

int GreaterthanConstant(struct BinarySearchTreeNode *r, int k){
	keysCount=0;
	while(r!=NULL){
		if(k < r->data){
			keysCount=keysCount+ r->right->size + 1;
			r=r->left;
		}
		else if(k > r->data)
			r=r->right;
		else{						//k==r->data
			keysCount= keysCount + r->right-size;
			break;
		}
	}
	return keysCount;
}		

13. Balanced Binary Search Trees

We reduce the worst case complexity (skew trees) from O(n) to O(logn) by imposing restrictions on the height. In general, the height balanced trees are represented with HB(k), where k is the difference between left subtree height and right subtree height and sometimes called balance factor.

Full Balanced Binary Search Tree
In HB(k), if k=0 (balance factor is zero), then we call such binary search trees as full balance binary search trees.

	   4
	 /    \
   2        6
  / \ 	   / \
 1   3    5   7
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值