Chapter 6: Trees Part I

Chapter 6: Trees Part I

1. Glossary

A tree structure is a way of representing the hierarchical nature of a structure in a graphical form.

  • The root of a tree is the node with no parents.
  • An edge refers to the link from parent child.
  • A node with no children is called leaf node.
  • A node p is an ancestor of node q if there exists a path from root to q and p appears on the path. The node q is called a descendant of p.
  • The set of all nodes at a given depth is called the level of the tree. The root node is at level zero.
  • The depth of a node is the length of the path from the root to the node.
  • The height of a node is the length of the path from that node to the deepest node. The height of a tree is the length of the path from the root to the deepest node in the tree.

2. Binary Trees

A tree is called binary tree if each node has zero child, one child or two children. Empty tree is also a valid binary tree.

Types

  • Strict Binary Tree: Each node has exactly two children or no children.
  • Full Binary Tree: Each node has exactly two children and all leaf nodes are at the same level.
  • Complete Binary Tree: All leaf nodes are at height h or h-1 and also without any missing number in the sequence.

Properties

Assume the height of tree is h h h with n n n nodes and the root node is at height zero.

  • The number of nodes in a full binary tree is 2 h + 1 − 1 2^{h+1} -1 2h+11.
  • The number of nodes in a complete binary tree is between 2 h 2^h 2h and 2 h + 1 − 1 2^{h+1}-1 2h+11.
  • The number of leaf nodes in a full binary tree is 2 h 2^h 2h
  • The number of NULL links (wasted pointers) in a complete binary tree of n n n nodes is n + 1 n+1 n+1.

Structure

struct BinaryTreeNode{
	int data;
	struct BinaryTreeNode *left;
	struct BinaryTreeNode *right;
};

Applications

  • Expression trees are used in compilers.
  • Huffman coding trees that are used in data compression algorithms.
  • Binary Search Tree (BST), which supports search, insertion and deletion on a collection of items in O(logn).
  • Priority Queue, which supports search and deletion of minimum on a collection of items in logarithmic time (worst case).

3. Binary Tree Traversals

The process of visiting all nodes of a tree is called tree traversal. Each node is processed only once but it may be visited more than once.

PreOrder Traversal

  1. Visit the root
  2. Traverse the left subtree in Preorder
  3. Traverse the right subtree in Preoder

To move to the right subtree after processing the left subtree, we must maintain the root information. The obvious ADT for such information is a stack. It is possible to get the information about the right subtrees back in the reverse order because of its LIFO structure.

void PreOrder(struct BinaryTreeNode *root){
	if(root){
		printf("%d", root->data);
		PreOrder(root->left);
		PreOrder(root->right);
	}
}
  • Time Complexity: O(n), Space Complexity: O(n).

Non-Recursive Preorder Traversal

void PreOrder(struct BinaryTreeNode *root){
	struct Stack *S=CreateStack();
	while(1){
		while(root){
			printf("%d", root->data);
			Push(S, root);
			root=root->left;		//if left subtree exists, add to stack
		}
		if(IsEmptyStack(S))
			break;
		root=Pop(S);
		root=root->right;			//indicates completion of left subtree and current node, now go to the right subtree
	}
	DeleteStack(S);
}	

InOrder Traversal

  1. Traverse the left subtree in Preorder
  2. Visit the root
  3. Traverse the right subtree in Preoder
void InOrder(struct BinaryTreeNode *root){
	if(root){
		InOrder(root->left);
		printf("%d", root->data);
		InOrder(root->right);
	}
}

Non-Recursive Inorder Traversal
The only change with previous approach is, instead of processing the node before going to left subtree, process it after popping which is indicated after completion of left subtree processing.

void InOrder(struct BinaryTreeNode *root){
	struct Stack *s=CreateStack();
	while(1){
		while(root){
			Push(s, root);
			root=root->left;
		}
		if(IsEmptyStack(s))
			break;
		root=Pop(s);
		printf("%d", root->data);	//Process the current node after popping		
		root=root->right);			//indicates completion of left subtree and current node, now go to the right subtree
	}
	DeleteStack(s);
}

PostOrder Traversal

  1. Traverse the left subtree in Preorder
  2. Traverse the right subtree in Preoder
  3. Visit the root
void PostOrder(struct BinaryTreeNode *root){
	if(root){
		InOrder(root->left);
		InOrder(root->right);
		printf("%d", root->data);
	}
}

Non-Recursive Postorder Traversal

In postorder traversal, each node is visited twice, that means after processing the left subtree we will visit the current node and after processing the right subtree we will visit the same current node but only process the node during the second visit. Here the problem is how to differentiate whether we are returning from left subtree or the right subtree.

We use a previous variable to keep track of the earlier traversed node. Assume current is the current node that is on top of the stack.

  • When previous is current’s parent, we are traversing down the tree. In this case, we try to traverse to current’s left child if available (push left child to the stack). If it is not available, we look at current’s right child. If both left and right child do not exist (leaf node), we print current’s value and pop it off the stack.
  • If previous is current’s left child, we are traversing up the tree from the left. We look at current’s right child. If it is available, then traverse down the right child; otherwise print current’s value and pop it off the stack. If previous is current’s right child, we are traversing up the tree from the right. In this case, we print current’s value and pop it off the stack.
void PostOrder(struct BinaryTreeNode *root){
	struct Stack *s=CreateStack();
	struct BinaryTreeNode *previous=NULL;
	do{
		while(root!=NULL){
			Push(s, root);
			root=root->left;
		}
		while(root==NULL && !IsEmptyStack(s)){
			root=Top(s);
			if(root->right==NULL || root->right==previous){
				printf("%d", root->data);
				Pop(s);
				previous=root;
				root=NULL;
			}
			else
				root=root->right;
		}
	}while(!IsEmptyStack(s));
}

Level Order Traversal

  1. Visit the root.
  2. While traversing level, keep all the elements at next level in queue.
  3. Go to the next level and visit all the nodes at that level.
  4. Repeat this until all levels are completed.
void LevelOrder(struct BinaryTreeNode *root){
	struct BinaryTreeNode *temp;
	struct Queue *Q=CreateQueue();
	if(!root) return;
	EnQueue(Q, root);
	while(!IsEmptyQueue(Q)){
		temp=DeQueue(Q);
		printf("%d", temp->data);
		if(temp->left)
			EnQueue(Q, temp->left);
		if(temp->right)
			EnQueue(Q, temp->right);
	}
	DeleteQueue(Q);
}	

4. Binary Trees: Problem & Solution

Pro 1.1: Find the maximum element in the binary tree

Solution 1: recursion

int FindMax(struct BinaryTreeNode *root){
	int rootval, left, right, max=INT_MIN;
	if(root){
		rootval=root->data;
		left=FindMax(root->left);
		right=FIndMax(root->right);
		max= left>right ? left : right;
		if(rootval>max)
			max=rootval;
	}
	return max;
}		
  • Time Complexity: O(n), Space Complexity: O(n)

Solution 2: level order traversal

Just observe the element while deleting.

int FindMax(struct BinaryTreeNode *root){
	struct BinaryTreeNode *temp;
	int max=INT_MIN;
	struct Queue *Q=CreateQueue();
	EnQueue(Q, root);
	while(!IsEmptyQueue(Q)){
		temp=DeQueue(Q);
		if(max<temp->data)
			max=temp->data;
		if(temp->left)
			EnQueue(Q, temp->left);
		if(temp->right)
			EnQueue(Q, temp->right);
	}
	DeleteQueue(Q);
	return max;
}	
  • Time Complexity: O(n), Space Complexity: O(n)

Pro 1.2:Search for an Element

Solution 1: recursion

int SearchElement(struct BinaryTreeNode *root, int data){
	int flag;
	if(!root) return 0;
	else{
		if(data==root->data)
			return 1;
		else{
			flag=SearchElement(root->left, data);
			if(flag!=0)
				return flag;
			else
				return SearchElement(root->right, data);
		}
	}
	return 0;
}			

Solution 2: level order traversal

int SearchElement(struct BinaryTreeNode *root, int data){
	struct BinaryTreeNode *temp;
	struct Queue *Q=CreateQueue();
	if(!root) return -1;
	EnQueue(Q, root);
	while(!IsEmptyQueue(Q)){
		temp=DeQueue(Q);
		if(data==root->data)
			return 1;
		if(temp->left)
			EnQueue(Q, temp->left);
		if(temp->right)
			EnQueue(Q, temp->right);
	}
	DeleteQueue(Q);
	return 0;
}

Pro 2: Insert an Element into Binary Tree

Solution : level order traversal
To insert an element, we can use the level order traversal and insert the element wherever we find the node whose left or right child is NULL.

void Insert(struct BinaryTreeNode *root, int data){
	struct Queue *Q=CreateQueue();
	struct BinaryTreeNode *temp, *newNode;
	newNode=(struct BinaryTreeNode *)malloc(sizeof(struct BinaryTreeNode));
	newNode->left=newNode->right=NULL;
	if(!newNode) printf("Memory Error");
	if(!root) root=newNode;
	EnQueue(Q, root);
	while(!IsEmptyQueue(Q)){
		temp=DeQueue(Q);
		if(temp->left)
			EnQueue(Q, temp->left);
		else{
			temp->left=newNode;
			DeleteQueue(Q);
			return;
		}
		if(temp->right)
			EnQueue(Q, temp->right);
		else{
			temp->right=newNode;
			DeleteQueue(Q);
			return;
		}
	}
	DeleteQueue(Q);
}
  • Time Complexity: O(n), Space Complexity: O(n)

Pro 3: Find the size of Binary Tree

Solution 1: recursively

int SizeOfBinaryTree(struct BinaryTreeNode *root){
	if(root==NULL) 
		return 0;
	else
		return (SizeOfBinaryTree(root->left) + SizeOfBinaryTree(root->right) + 1 );
}

Solution 2: level order traversal

int SizeOfBinaryTree(struct BinaryTreeNode *root){
	struct BinaryTreeNode *temp;
	struct Queue *Q=CreateQueue();
	int count=0;
	if(!root) return 0;
	EnQueue(Q, root);
	while(!IsEmptyQueue(Q)){
		temp=DeQueue(Q);
		count++;
		if(temp->left)
			EnQueue(Q, temp->left);
		if(temp->right)
			EnQueue(Q, temp->right);
	}
	DeleteQueue(Q);
	return count;
}

Pro 4: Print the Level Order Data in Reverse Order

Solution : auxiliary stack

void LevelOrderTraversalReverse(struct BinaryTreeNode *root){
	struct Queue *Q=CreateQueue();
	struct Stack *S=CreateStack();
	struct BinaryTreeNode *temp;
	if(!root) return;
	EnQueue(Q, root);
	while(!IsEmptyQueue(Q)){
		temp=DeQueue(Q);
		if(temp->right)
			EnQueue(Q, temp->right);
		if(temp->left)
			EnQueue(Q, temp->left);
		Push(S, temp);
	}
	while(!IsEmptyStack(S))
		printf("%d", Pop(S)->data);
}

Pro 5: Delete the Tree

Solution : Post Order Traversal

Before deleting the parent node we should delete its children nodes first. We can use postorder traversal as it does the work without storing anything.

void DeleteBinaryTree(struct BinaryTreeNode *root){
	if(root==NULL) return;
	DeleteBinaryTree(root->left);
	DeleteBinaryTree(root->right;
	free(root);										//delete current node only after deleting subtree
}

Pro 6.1: Find the Height (or Depth) of Binary Tree

Solution 1: recursively / DFS

Recursively calculate height of left and right subtrees of a node and assign height to the node as max of the heights of two children plus one. This is similar to PreOrder tree traversal (and DFS of Graph algorithms).

int Height(struct BinaryTreeNode *root){
	int leftH, rightH;
	if(root==NULL) 
		return 0;
	else{
		leftH=Height(root->left);
		rightH=Height(root->right);
		return leftH>rightH ? leftH+1 :rightH+1;
	}
}

Solution 2: level order traversal / BFS

int Height(struct BinaryTreeNode *root){
	int level=0;
	struct Queue *Q=CreateQueue();
	if(!root) return 0;
	EnQueue(Q, root);
	EnQueue(Q, NULL);							//end of first level
	while(!IsEmptyQueue(Q)){
		root=DeQueue(Q);
		if(root==NULL){							//completion of current level
			if(!IsEmptyQueue(Q))
				EnQueue(Q, NULL);				//put another mark/flag for next level
			level++;
		}
		else{
			if(root->left)
				EnQueue(Q, root->left);
			if(root->right)
				EnQueue(Q, root->right);
		}
	}
	return level;
}

Pro 6.2 Fill the Next Sibling Pointers

Given a binary tree with three pointers (left, right, and nextSibling), give an algorithm for filling the nextSibling pointers assuming they are NULL initially.

Solution 1: queue / level order traversal

struct BinaryTreeNode{
	struct BinaryTreeNode *left, *right;
	struct BinaryTreeNode *nextSibling;
};
int FillNextSibling(struct BinaryTreeNode *root){
	if(!root) return 0;
	struct BinaryTreeNode *temp;
	struct Queue *Q=CreateQueue();
	EnQueue(Q, root);
	EnQueue(Q, NULL);
	while(!IsEmptyQueue(Q)){
		temp=DeQueue(Q);
		if(temp==NULL){
			if(!IsEmptyQueue(Q))
				EnQueue(Q, NULL);					//put another marker for next level
		}
		else{
			temp->nextSibling=QueueFront(Q);
			if(root->left)
				EnQueue(Q, temp->left);
			if(root->right)
				EnQueue(Q, temp->right);
		}
	}
}		

Solution 2: reuse the populated nextSibling pointers

  • Before we pass the left and right to the recursion function itself, we connect the right child’s nextSibling to the current node’s nextSibling left child. In order for this to work, the current node nextSibling pointer must be populated, which is true in this case.
												    o  ->NULL
												  /    \
												o   ->   o ->NULL
											   / \      / \
											 o -> o -> o -> o ->NULL
void FillNextSibling(struct BinaryTreeNode *root){
	if(!root) return;
	if(root->left) 
		root->left->nextSibling=root->right;
	if(root->right)
		root->right->nextSibling=(root->Sibling) ? root->nextSibling->left : NULL;
	FillNextSibling(root->left);
	FillNextSibling(root->right);
}

Pro 6.3 Find the Level with Maximum Sum

Solution :

int FindLevelwithMaxSum(struct BinaryTreeNode *root){
	if(!root) return 0;
	struct BinaryTreeNode *temp;
	int level=0, maxLevel=0;
	int currentSum=0, maxSum=0;
	struct Queue *Q=CreateQueue();
	EnQueue(Q, root);
	EnQueue(Q, NULL); 						//Flag / Mark indicates end of the level
	while(!IsEmptyQueue(Q)){
		temp=DeQueue(Q);
		if(temp==NULL){
			if(currentSum>maxSum){
				maxSum=currentSum;
				maxLevel=level;			
			}
			currentSum=0;					//reset
			if(!IsEmptyQueue(Q))
				EnQueue(Q, NULL);			//indicate new level
			level++;
		}
		else{
			currentSum+=temp->data;
			if(temp->left)
				EnQueue(Q, temp->left);
			if(temp->right)
				EnQueue(Q, temp->right);
		}
	}
	return maxLevel;
}

Pro 7.1: Find the Deepest Node of the Binary Tree

Solution :

struct BinaryTreeNode *DeepestNode(struct BinaryTreeNode *root){
	if(!root) return NULL;
	struct BinaryTreeNode *temp;
	struct Queue *Q=CreateQueue();
	EnQueue(Q, root);
	while(!IsEmptyQueue(Q)){
		temp=DeQueue(Q);
		if(temp->left)
			EnQueue(Q, temp->left);
		if(temp->right)
			EnQueue(Q, temp->right);
	}
	DeleteQueue(Q);
	return temp;
}

Pro 7.2: Delete an Element from Binary Tree

Solution :

  • Algorithm:
1. Starting at root, find the node which we want to delete.
2. Find the deepest node in the tree.
3. Replace the deepest node's data with node to be deleted.
4. Then delete the deepest node.

Pro 8.1: Find the Number of Leaves Without using Recursion

The set of nodes whose both left and right children are NULL are called leaf nodes.

Solution : level order traversal

int NumsOfLeaves(struct BinaryTreeNode *root){
	if(!root) return 0;
	struct BinaryTreeNode *temp;
	struct Queue *Q=CreateQueue();
	int count=0;
	EnQueue(Q, root);
	while(!IsEmptyQueue(Q)){
		temp=DeQueue(Q);
		if(!temp->left && !temp->right)
			count++;
		else{
			if(temp->left)
				EnQueue(Q, temp->left);
			if(temp->right)
				EnQueue(Q, temp->right);
		}	
	}
	DeleteQueue(Q);
	return count;
}

Pro 8.2: Find the Number of Full Nodes Without using Recursion

The set of all nodes with both left and right children are called full nodes.

Solution : level order traversal

int NumsOfFullNodes(struct BinaryTreeNode *root){
	if(!root) return 0;
	struct BinaryTreeNode *temp;
	struct Queue *Q=CreateQueue();
	int count=0;
	EnQueue(Q, root);
	while(!IsEmptyQueue(Q)){
		temp=DeQueue(Q);
		if(temp->left && temp->right)
			count++;
		if(temp->left)
			EnQueue(Q, temp->left);
		if(temp->right)
			EnQueue(Q, temp->right);
	}
	DeleteQueue(Q);
	return count;
}

Pro 8.3: Find the Number of Half Nodes Without using Recursion

The set of all nodes with either left or right children are called half nodes.

Solution : level order traversal

int NumOfHalfNodes(struct BinaryTreeNode *root){
	if(!root) return 0;
	struct BinaryTreeNode *temp;
	struct Queue *Q=CreateQueue();
	int count=0;
	EnQueue(Q, root);
	while(!IsEmptyQueue(Q)){
		temp=DeQueue(Q);
		if((!temp->left && temp->right) || (temp->left && !temp->right))
			count++;
		if(temp->left)
			EnQueue(Q, temp->left);
		if(temp->right)
			EnQueue(Q, temp->right);
	}
	DeleteQueue(Q);
	return count;
}

Pro 9: Compare Two Binary Tree Structure

Solution : recursion

int WhetherStructureIdentical(struct BinaryTreeNode *root1, struct BinaryTreeNode *root2){
	if(root1==NULL && root2==NULL)
		return 1;
	if(root1==NULL || root2==NULL)
		return 0;
	return (root1->data==root2->data && WhetherStructureIdentical(root1->left, root2->left) && WhetherStructureIdentical(root1->right, root3->right));
}

Pro 10: Find the Diameter / Width of the Binary Tree

The diameter of a tree is the number of nodes on the longest path between two leaves in the tree.

Solution 1:

int DiameterOfTree(struct BinaryTreeNode *root, int *ptr){
	if(!root) return 0;
	int left=DiameterOfTree(root->left, ptr);
	int right=DiameterOfTree(root->right, ptr);
	if(left+right>*ptr)
		*ptr=left+right;
	return Max(left, right)+1;
}
/*Alternative Codeing*/
static int Diameter(struct BinaryTreeNode *root){
	if(root==NULL) return 0;
	int lHeight=height(root->left);
	int rHeight=height(root->right);
	int lDiameter=Diameter(root->left);
	int rDiameter=Diameter(root->right);
	return Max(lHeight+rHeight+1, Max(lDiameter, rDiameter));
}
static int height(BinaryTreeNode *root){
	if(root==NULL) return 0;
	return 1+Max(height(root->left), height(root->right));
}

Solution 2:

  • Add two extra variables in the node structure to stores its left child’s and right child’s maximum diameter so that there is no need to recursively call the height method.
int DiameterOfTree(BinaryTreeNode *root){
	int nMaxLen=0;
	if(root==NULL) return 0;
	if(root->left==NULL) root->nMaxLeft=0;
	else DiameterOfTree(root->left);
	if(root->right==NULL) root->nMaxRight=0;
	else DiameterOfTree(root->right);
	if(root->left){
		int nTempMaxLen= (root->left->nMaxLeft > root->left->nMaxRight) ? root->left->nMaxLeft : root->left->nMaxRight;
		root->nMaxLeft=nTempMaxLen+1;
	}
	if(root->right){
		int nTempMaxLen= (root->right->nMaxLeft > root->right->nMaxRight) ? root->right->nMaxLeft : root->right->nMaxRight;
		root->nMaxRight=nTempMaxLen+1;
	}
	nMaxLen=Max(root->nMaxLeft + root->nMaxRight, nMaxLen);
	return nMaxLen;
}

Pro 11: Print Out All Root-to-Leaf Paths

Solution : Recursively

void TreePath(struct BinaryTreeNode *root, int path[], int pathLen){
	if(root==NULL) return;
	/*append this node to path array*/
	path[pathLen]=root->data;
	pathLen++;
	/*if it's a leaf (reach end) then print*/
	if(!root->left && !root->right)
		printArray(path, pathLen);
	else{
		TreePath(root->left, path, pathLen);
		TreePath(root->right, path, pathLen);
	}
}
void printArray(int ints[], int len){
	for(int i=0; i<len; i++)
		printf("%d", ints[i]);
	printf("\n");
}	

Pro 12: Check the Existence of Path with Given Sum

Solution : recursion

  • The strategy is, subtract the node value from the sum before calling its children recursively and check to see if the sum is zero when we run out of tree.
void HasPathSum(BinaryTreeNode *root, int sum){
	if(root==NULL) 
		return false;
	int data=root->data;
	if(root->left==NULL && root->right==NULL && data=sum
		return true;
	return HasPathSum(root->left, sum-data) || HasPathSum(root->left, sum-data);
}

Pro 13: Find Sum of all Element

Solution 1: recursion

int SumOfElemet(struct BinaryTreeNode *root){
	if(!root) return 0;
	else return (root->data + SumOfElements(root->left) + SumOfElements(root->right));
}	

Solution 2: level order traversal

int SumOfElements(struct BinaryTreeNode *root){
	if(!root) return 0;
	struct BinaryTreeNode *temp;
	struct Queue *Q=CreateQueue();
	int sum=0;
	EnQueue(Q, root);
	while(!IsEmptyQueue(Q)){
		temp=DeQueue(Q);
		sum+=temp->data;
		if(temp->left)
			EnQueue(Q, temp->left);
		if(temp->right)
			EnQueue(Q, temp->right);
	}
	DeleteQueue(Q);
	return sum;
}

Pro 14.1: Convert a Tree to its Mirror

Mirror of a tree is another tree with left and right children of all non-leaf nodes interchanged.

Solution :

struct BinaryTreeNode *MirrorOfTree(struct BinaryTreeNode *root){
	struct BinaryTreeNode *temp;
	if(root){
		MirrorOfTree(root->left);
		MirrorOfTree(root->right);
		temp=root->left;
		root->left=root->right;
		root->right=temp;
	}
	return root;
}

Pro 14.2: Check whether two trees are Mirrors of each other

Solution :

int WhetherMirror(struct BinaryTreeNode *root1, struct BinaryTreeNode *root2){
	if(root1==NULL && root2==NULL)
		return 1;
	if(root1==NULL || root2==NULL)
		return 0;
	if(root1->data != root2->data)
		return 0;
	else 
		return WhetherMirror(root1->left, root2->right) && WhetherMirror(root1->right, root2->left);
}	

Pro 15. Find LCA (Least Common Ancestor of two nodes

Solution :

  • The least common ancestor x satisfy condition: (flson && frson) || ((x=a || x=b) &&(flson || frson))
    fx represents whether node x’s subtree include node a or b.
    lson and rson represent left children and right children respectively.
    (flson && frson) indicates a case that both left subtree and right subtree contain node a or b.
    ((x=a || x=b) &&(flson || frson)) indicates a case that x is a and b is in the subtree of x.
										   o				  a						
										 /   \ 				/	\
										a     b			   o	  b
struct BinaryTreeNode *LCA(struct BinaryTreeNode *root, struct BinaryTreeNode *a, struct BinaryTreeNode *b){
	struct BinaryTreeNode *left, *right;
	if(root==NULL)
		return root;
	if(root==a||root==b)
		return root;
	left=LCA(root->left, a, b);
	right=LCA(root->right, a, b);
	if(left && right)
		return root;							//in two subtree respectively
	else
		return left ? left : right;				//both in one side of subtree
}

Pro 16: Construct Tree from Given Inorder and Preorder Traversal

Consider the traversals below:
Inorder sequence: D B E A F C.
Preorder sequence: A B D E C F.

Solution :

  • In a Preorder sequence, leftmost element denotes the root of the tree. In a Inorder sequence, all elements on the left side of root come under the left subtree and so does the right side.
  • Algorithm:
1. Select an element from Preorder. Increment PreOrderIndex variable to pick next element in next recursive call.
2. Create a new tree node from heap with data as selected element.
3. Call BuildBinaryTree() for elements before InOrderIndex and make the built tree as left subtree of newNode.
4. Call BuildBinaryTree() for elements after InOrderIndex and make the built tree as right subtree of newNode.
5. return newNode.
struct BinaryTreeNode *BuildBinaryTree(int inOrder[], int preOrder[], int inOrderStart, int inOrderEnd){
	if(inOrderStart>inOrderEnd) return NULL;
	static int preOrderIndex=0;
	struct BinaryTreeNode *newNode=(struct BinaryTreeNode *)malloc(sizeof(struct BinaryTreeNode));
	if(!newNode) return NULL;							//memory error
	
	/*select curren node from preorder traversal using PreOrderIndex*/
	newNode->data=preOrder[preOrderIndex];
	++preOrderIndex;
	if(inOrderStart==inOrderEnd)
		return newNode;
	
	/*find the index of this node in InOrder traversal*/
	int inOrderIndex=Seach(inOrder, inOrderStart, inOrderEnd, newNode->data);

	/*fill the left and right subtrees using index in Inorder traversal*/
	newNode->left=BuildBinaryTree(inOrder, preOrder, inOrderStart, inOrderIndex-1);
	newNode->right=BuildBinaryTree(inOrder, preOrder, inOrderIndex+1, inOrderEnd);
	return newNode;
}
/*textbook version lock of Search() function*/
/*acwing version cpp*/
class Solution{
public:
	unordered_map<int, int> pos;				
	TreeNode* buildTree(vector<int> &preorder, vector<int> &inorder){
		int n=preorder.size();
		for(int i=0; i<n; ++i)
			pos[inorder[i]]=i;					//record the value position in the inorder traversal
		return dfs(preorder, inorder, 0, n-1, 0, n-1);
	}
	TreeNode* dfs(vector<int> &pre, vector<int> &in, int pl, int pr, int il, int ir){
		if(pl>pr) return nullptr;
		int k=pos[pre[pl]]-il;
		TreeNode* root=new TreeNode(pre[pl]);
		root->left=dfs(pre, in, pl+1, pl+k, il, il+k-1);
		root->right=dfs(pre, in, pl+k+1, pr, il+k+1, ir);
		return root;
	}
};
/*
	inorder [inLeft, root-1][root][root+1, inRight]
	assume preorder's left subtree's broder is x, we have x-(preLeft+1)=root-1-inLeft 
	preorder[preLeft][preLeft+1, root-inLeft+preLeft][root-inLeft+preLeft+1, preRight]
*/		

Pro 16: Print All the Ancestors of a Node

Solution :

int PrintAllAncestor(struct BinaryTreeNode *root, struct BinaryTreeNode *node){
	if(root==NULL) return 0;
	if(root->left==node || roo->right==node || PrintAllAncestor(root->left, node) || PrintAllAncestor(root->right, node)){
		printf("%d", root->data);
		return 1;
	}
	return 0;
}

Pro 17: Zigzag Tree Traversal

The output of the tree below in zigzag order is: 1, 3, 2, 4, 5, 6, 7.
在这里插入图片描述
Solution : two stacks

void ZigZagTraversal(struct BinaryTreeNode *root){
	struct BinaryTreeNode *temp;
	int leftToRight=1;
	if(!root) return;
	struct Stack *currentLevel=CreateStack(), *nextLevel=CreateStack();
	Push(currentLevel, root);
	while(!IsEmptyStack(currentLevel){
		temp=Pop(currentLevel);
		if(temp){
			printf("%d", temp->data);
			if(leftToRight){
				if(temp->left)
					Push(nextLevel, temp->left);
				if(temp->right)
					Push(nextLevel, temp->right);
			}
			else{
				if(temp->right)
					Push(nextLevel, temp->right);
				if(temp->left)
					Push(nextLevel, temp->left);
			}
		}
		if(IsEmptystack(currentLevel)){
			leftToRight=1-leftToRight;
			swap(currentLevel, nextLevel);
		}	
	}
}

Pro 18: How Many Different Binary Trees are Possible with n Nodes

Solution :

In general, if there are n nodes, there exist 2 n − n 2^n-n 2nn different trees.

Pro 19: Construct the Tree with a Special Property

Given a tree with special property where leaves are represented with ‘L’ and internal node with ‘I’. Assume that each node has either 0 or 2 children. Given preorder of this tree, construct the tree.

Given preorder string : ILILL.
在这里插入图片描述
Solution :

  • In a normal scenario, it’s impossible to detect where left subtree ends and right subtree starts using only preorder traversal. Since every node has either 2 children or no child, we can surely say that if a node exists then its sibling also exists. So every time when we are computing a subtree, we need to compute its sibling subtree as well.
  • Whenever we get ‘L’ in the input string, that is a leaf and we stop for a particular subtree at that point. After this ‘L’ node (left child of its parent ‘L’), its sibling starts. If ‘L’ node is right child of its parent, then we need to go up in the hierarchy to find the next subtree to compute.
struct BinaryTreeNode *BuildTree(char *A, int *i){
	struct BinaryTreeNode *newNode=(struct BinaryTreeNode *)malloc(sizeof(struct BinaryTreeNode));
	newNode->data=A[*i];
	newNode->left=newNode->right=NULL;
	if(A==NULL){
		free(newNode);
		return NULL;								//boundary condition
	}
	if(A[*i]=='L')
		return newNode;								//reach leaf node
	*i=*i+1;
	newNode->left=BuildTree(A, i); 					//populate left subtree
	*i=*i+1;
	newNode->right=BuildTree(A, i);					//populate right subtree
	return newNode;	
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
《随机信号分析》(Chapter 6)是王永德编写的一本关于随机信号分析的教材,该章节的主要内容是关于信号的功率谱密度函数(PSD)的分析。 在信号处理领域,随机信号是一种在时间和幅度上都是随机变化的信号。对于随机信号的分析,其中一个重要的概念就是功率谱密度函数。功率谱密度函数可以用来描述信号的功率在频域中的分布情况,是信号在不同频率上的功率密度。 在第6章中,王永德首先介绍了图像的基本概念,包括平均值、自相关函数等。然后,他引入了功率谱密度函数的定义,并介绍了如何通过傅里叶变换将信号从时域转换到频域。接着,他详细讲解了如何计算信号在频域上的功率谱密度函数,并给出了一些常见信号的功率谱密度函数的例子。 在随后的内容中,王永德还介绍了如何通过对随机信号的平均值和自相关函数进行估计来估计功率谱密度函数。他解释了如何使用周期图和Welch方法来估计功率谱密度函数,并介绍了这些方法的优缺点。 最后,王永德还介绍了一些关于功率谱密度函数的统计性质,包括自相关函数与功率谱密度函数的傅里叶变换关系以及功率谱密度函数的线性性质等。 总的来说,通过《随机信号分析》(Chapter 6)这一章节的学习,读者可以了解到随机信号分析中功率谱密度函数的基本概念、计算方法及其在信号处理中的应用,为进一步深入学习随机信号分析打下了坚实的基础。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值