Space complexity of isSubtree function for 2 binary trees - algorithm

public boolean isSubtree(TreeNode root, TreeNode subRoot) {
if (root == null) return false;
return areSame(root, subRoot) || isSubtree(root.left, subRoot) || isSubtree(root.right, subRoot);
}
public boolean areSame(TreeNode root, TreeNode subRoot) {
if (root == null && subRoot == null) return true;
if (root == null || subRoot == null) return false;
if (subRoot.val != root.val)
return false;
return areSame(root.left, subRoot.left) && areSame(root.right, subRoot.right);
}
Is the space compelxity of my above solution to find if a tree is subtree of another binary tree - O(height(tree1)) (as suggested in most of the discussion comments) or O(height(tree1)+ height(tree2)) where
I think it should be O(height(tree1) + height(tree2)) because isSubtree can go as deep as one branch of tree1, and for each call, the isSame() could go as deep as height(tree2), so the maximum stack memory being used at any instant would be ht1+ht2.

Assuming that the boolean operators && and || are short-circuiting operators (as they are in Java), the recursion depth (and extra stack memory) is upper bounded by O(height(tree1)).
Since isSubtree(root, subRoot) can only call itself (with the first tree's height reduced by 1) or areSame(root, subRoot), and at most one call of 'areSame' directly from 'isSubtree' can be on the stack at a time (because of short-circuiting), the recursion depth is O(height(tree1)) + max-depth-of(areSame(tree1, tree2)).
Now, areSame(root, subRoot) makes no recursive calls if root is null. If root is not null, it may call:
areSame(root.left, subRoot.left) && areSame(root.right, subRoot.right);
Here, it calls areSame only with child nodes of root: the height of the first tree has been reduced by 1, and the first call must complete before the second call starts (since && short-circuits). So there can be at most height(tree1) + 1 calls to areSame on the call-stack at any time, so the total recursion depth/ stack space of isSubtree is O(height(tree1))

Related

Leetcode 572: subtree of another tree run time and space analysis

I have question on whether my analysis of runtime and space for algorithm that determine whether a tree S contains a subtree that is exactly the same tree as other tree T My code is as follows:
class Solution {
public boolean isSubtree(TreeNode root, TreeNode subRoot) {
if(root == null) return subRoot == null;
return isSameTree(root, subRoot) ||
isSubtree(root.left, subRoot) ||
isSubtree(root.right, subRoot);
}
public boolean isSameTree(TreeNode root, TreeNode subRoot) {
if(root == null) return subRoot == null;
if(subRoot == null) return false;
return root.val == subRoot.val &&
isSameTree(root.left, subRoot.left) && isSameTree(root.right, subRoot.right);
}
}
I think the time is O(S * min(S, T)) where S and T are total number of node of tree s and t respectively, and space is O(max(S, T)). I got the time because we are performing dfs on every node of tree s to determine whether it is the same as tree t, and each dfs takes minimum of node of s and t, and space is because our recursion call stack can at most contain minimum of two tree's node, if smaller tree hits null case while dfsing with larger tree, we will just return
You don't need to check on subRoot being null as it is invalid that null is subtree of null. So, in that case you can return false. Please use this code for your reference and time
complexity O(mn), where m = |nodes| ∈ root and n = |nodes| ∈ subRoot
class Solution {
public boolean isSubtree(TreeNode root, TreeNode subRoot) {
if(root == null)
return false;
if (isSameTree(root, subRoot))
return true;
return isSubtree(root.left, subRoot) || isSubtree(root.right, subRoot);
}
public boolean isSameTree(TreeNode root, TreeNode subRoot) {
if(root==null || subRoot==null)
return root==subRoot;
return root.val == subRoot.val && isSameTree(root.left, subRoot.left) && isSameTree(root.right, subRoot.right);
}
}

is wikipedia iterative postorder tree traversal pseudo code wrong?

Here is the pseudo code that wikipedia gives for iterative postorder tree traversal.
iterativePostorder(node)
parentStack = empty stack
lastnodevisited = null
while (not parentStack.isEmpty() or node ≠ null)
if (node ≠ null)
parentStack.push(node)
node = node.left
else
peeknode = parentStack.peek()
if (peeknode.right ≠ null and lastnodevisited ≠ peeknode.right)
/* if right child exists AND traversing node from left child, move right */
node = peeknode.right
else
visit(peeknode)
lastnodevisited = parentStack.pop()
It is pretty straight forward, and I have implemented it in Java. But it does not work, the problem is that every time it visits the most left leaf and return to its parent, it will add that left leaf again into the stack in next iteration. This causes an infinite loop. Is my method incorrect or the wikipedia version is wrong?
public static List<Integer> postorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<Integer>();
if (root == null) return res;
Stack<TreeNode> s = new Stack<TreeNode>();
TreeNode lastVisitedNode = null;
TreeNode curr = root;
int i = 0;
while (curr != null || !s.isEmpty()) {
if (curr != null) {
System.out.println("push " + curr.val);
s.push(curr);
curr = curr.left;
} else {
curr = s.peek();
if (curr.right != null && lastVisitedNode != curr.right) {
curr = curr.right;
} else {
res.add(curr.val);
System.out.println("pop " + curr.val);
lastVisitedNode = s.pop();
}
}
System.out.println(s);
System.out.println(res);
if (i>8) break;
else i++;
}
return res;
}
The wikipedia version is wrong for the exact same reason as you've explained it.
Here is a probably better pseudo-code, from geeksforgeeks
1.1 Create an empty stack
2.1 Do following while root is not NULL
a) Push root's right child and then root to stack.
b) Set root as root's left child.
2.2 Pop an item from stack and set it as root.
a) If the popped item has a right child and the right child
is at top of stack, then remove the right child from stack,
push the root back and set root as root's right child.
b) Else print root's data and set root as NULL.
2.3 Repeat steps 2.1 and 2.2 while stack is not empty.
You will have to add extra code to check if the node right child is null in 2.1.a though.
The wikipedia pseudocode is not wrong. They use two different variables: node and peekNode, while you only use curr for both. Node == null refers to the case when there is no more of a left branch left to explore, so we can stop pushing and instead investigate the next element in the stack. You can either revert to using two different variables, or you make the following fix in your code:
Since you reassign curr to a non-null value everytime you investigate the stack, you need to reset curr to null after you visit your node. (Because the state that still no more left branch left to explore is still unchanged).
The wikipedia pseudocode doesn't have to do this because their node value remains null.
Here is my code which gives a perfect answer:
var currentNode = this.root()
var previousNode = null
while(!nodeStack.isEmpty() || currentNode) {
// If there is a node on which the recursive call is made, we have a subtree to explore. If this is null, we have to backtrack and do a callback.
if (currentNode) {
nodeStack.push(currentNode)
previousNode = currentNode
currentNode = currentNode.leftChild
} else {
currentNode = nodeStack.peek()
if (currentNode.rightChild && previousNode != currentNode.rightChild) {
currentNode = currentNode.rightChild
} else {
callback(currentNode)
currentNode = null
previousNode = nodeStack.pop()
}
}
}

Get the distance from a root to a node with given value in a binary tree: Algorithm correctness

Here's my code
public int getDist(Node root, int value)
{
if (root == null && value !=0)
return -1;//
if(root.value == value)// we have a match
return 0;
if(root.getLeft()!=null)
int left =1+ getDist(root.getLeft(),value);
int right = 1+getDist(root.getRight(),value);
if(left ==-1 && right== -1)
return -1;//not found
return Math.max(left,right);
}
I would appreciate any feedback on the correctness of the above approach or any optimizations.
As it stands your code won't work as intended. Consider this on the other hand:
public int getDist(Node root, int value) {
// value is never in an empty subtree
if (root == null)
return -1;
// found value, distance from root is 0
if(root.value == value)
return 0;
// find distance from root of left subtree
int left = getDist(root.getLeft(),value);
// find distance from root of right subtree
int right = getDist(root.getRight(),value);
// value not found in either subtree
if(left == -1 && right == -1)
return -1;
// if the value was found,
// return the distance from the root of the subtree + 1
return 1 + Math.max(left,right);
}
All I changed was remove some superfluous checks and move the +1 after the check for "value not in either subtree". The effect this has is the following: if the recursion finds that the value is not in a subtree, then the return statements will ripple up the value -1 all the way to the root of the subtree without changing it, keeping the information "value not here" intact. If the value was found in at least one subtree, then it can't be that both left and right are -1 so that check will fail and the return statement at the end will give back the intended value.
Let int? indicate a variant type that is either an int or null. In C# this is Nullable<int>, in C++11 this is std::optional<int>, etc.
Then the following code will work. It is only slightly modified from your code, with the key difference being the use of min instead of max.
int? dist(Node root, int value)
{
if (root == null) return null;
if (root.value == value) return 0;
int? left = dist(root.left, value);
int? right = dist(root.right, value);
if (left == null && right == null) return null;
if (left != null && right != null) return 1 + min(left, right);
if (left != null) return 1 + left;
return 1 + right;
}
If you want, you can just use int and replace the appropriate occurrences of null with some special value like -1. It's my personal preference to use a nullable/optional value because it seems clearer, but it's by no means required for this problem.

number of leaves in a binary tree

I am a beginner to binary trees and have been working my way through the algorithms book. I have learnt about the various traversal methods of BSTs (pre-order, post order etc).
Could someone please explain how one can traverse a BST to count the number of nodes that are leaves (no children) please?
Many thanks!
Use a recursive method:
For a leaf return 1.
For a non-leaf, return the sum of that method applied to its children.
Example in PHP:
class BST {
public $left; // The substree containing the smaller entries
public $right; // The substree containing the larger entries
public $data; // The value that is stored in the node
}
function countLeafs(BST $b) {
// Test whether children exist ...
if ($b->left || $b->right) {
// ... yes, the left or the right child exists. It's not a leaf.
// Return the sum of calling countLeafs() on all children.
return ($b->left ? countLeafs($b->left) : 0)
+ ($b->right ? countLeafs($b->right) : 0);
} else {
// ... no, it's a leaf
return 1;
}
}
The different traversal methods would lead to different algorithms (although for a simple problem like this, all DFS variants are more or less the same).
I assume that you have a BST which consists of objects of type Node. A node has two fields left and right of type Node, which are the children of the node. If a child is not present, the value of that field is null. The whole tree is referenced by a reference to the root, called root. In java:
class Node {
public Node left;
public Node right;
}
Node root;
A DFS is easiest to implement by recursion: define a method
int numberOfLeafs(Node node)
which returns the number of leafs in the subtree rooted by node. Of course, numberOfLeafs(root) should yield the number of leafs of the whole tree.
As said, it is really artificial to distinguish pre-, in-, and post-order traversal here, but I'm gonna do it anyway:
Pre-order DFS: First deal with the current node, then with the children
int numberOfLeafs(Node node) {
int result = 0;
if (node.left == null && node.right == null)
result += 1;
if (node.left != null)
result += numberOfLeafs(node.left)
if (node.right != null)
result += numberOfLeafs(node.right)
return result;
}
In-order DFS: First deal with the left child, then with the current node, then with the right child
int numberOfLeafs(Node node) {
int result = 0;
if (node.left != null)
result += numberOfLeafs(node.left)
if (node.left == null && node.right == null)
result += 1;
if (node.right != null)
result += numberOfLeafs(node.right)
return result;
}
Post-order DFS: First deal with the children, then with the current node
int numberOfLeafs(Node node) {
int result = 0;
if (node.left != null)
result += numberOfLeafs(node.left)
if (node.right != null)
result += numberOfLeafs(node.right)
if (node.left == null && node.right == null)
result += 1;
return result;
}
For a BFS, you typically use a simple loop with a queue in which you add unvisited vertices. I now assume that I have a class Queue to which I can add nodes at the end and take nodes from the front:
Queue queue = new Queue();
queue.add(root);
int numberOfLeafs = 0;
while (!queue.empty) {
// take an unhandled node from the queue
Node node = queue.take();
if (node.left == null && node.right == null)
numberOfLeafs += 1;
if (node.left != null)
queue.add(node.left);
if (node.right != null)
queue.add(node.right);
}
try this
int countLeafNodes(BTNode node) {
if (node == null)
return 0;
if (node.getLeftChild() == null && node.getRightChild() == null
&& node.getParent() != null)//this is a leaf, no left or right child
return 1;
else
return countLeafNodes(node.getLeftChild())
+ countLeafNodes(node.getRightChild());
}
which recursively counts leaf nodes for left and right sub trees and returns the total count

Is Doubly link list OR BST

Given a node with the following structure
class Node {
int data,
Node* P1,
Node* p2;
}
We need to determine, if the node represents a Circular Doubly Link List OR a Binary Tree.
In my opinion We need to start traversing the given node in one direction
node = givenNode;
while(node->P1 != null && node->P1 != givenNode)
{
node = node->p1
}
if(node == givenNode) // It means Circular DLL
else if(node == null) // It means Tree
And it would take O(n) time to detect this.
Please suggest if there is any better approach than this.
I suggest you could check if its a doubl-linked list or not with this piece of code:
node = givenNode;
if(givenNode->P1 == null || givenNode->P2 == null)
// It can not be double link list (circular)
else if(givenNode->p1->p2 == givenNode || givenNode->p2->p1 == givenNode)
{
//It is a double linked list
}
else
{
It is not a double linked list
}
And we have O(1) complexity

Resources