Why the run time of similar solution is so different? - c++

There is a problem in LeetCode. I use a straightforward recursive solution to solve it, but the run time is to long, which is 170ms. Then I found a similar solution, which also is recursive, and the run time of this is just about 10ms. Why?
My solution:
class Solution
{
public:
bool isBalanced(TreeNode* root)
{
if (root == nullptr)
return true;
bool left = isBalanced(root->left);
bool right = isBalanced(root->right);
bool curr = false;
int sub = height(root->left) - height(root->right);
if (sub < 2 && sub > -2)
curr = true;
return left && right && curr;
}
private:
int height(TreeNode* root)
{
if (root == nullptr)
return 0;
int leftHeight = height(root->left);
int rightHeight = height(root->right);
return (leftHeight > rightHeight) ? (leftHeight + 1) : (rightHeight + 1);
}
};
The fast solution I found:
class Solution {
public:
bool isBalanced(TreeNode *root) {
if (root==NULL) return true;
int left = treeDepth(root->left);
int right = treeDepth(root->right);
if (left-right>1 || left-right < -1) {
return false;
}
return isBalanced(root->left) && isBalanced(root->right);
}
int treeDepth(TreeNode *root) {
if (root==NULL){
return 0;
}
int left=1, right=1;
left += treeDepth(root->left);
right += treeDepth(root->right);
return left>right?left:right;
}
};
Thank you!

Your solution calls both isBalanced and height, always. For every single node in the tree.
The faster solution calls treeDepth for every single node, but bails out early and doesn't call isBalanced if it knows the tree is not balanced. It's an optimization to not call unnecessary (recursive/expensive) functions.

Related

ERROR: Abort signal from abort(3) (SIGABRT) on "Burning Tree" challenge

I am looking at the Geeks for Geeks problem Burning Tree:
Given a binary tree and a node called target. Find the minimum time required to burn the complete binary tree if the target is set on fire. It is known that in 1 second all nodes connected to a given node get burned. That is its left child, right child, and parent.
I am getting the following error:
Abort signal from abort(3) (SIGABRT)
This is my code:
class Solution {
// mapping to parent
// returns the target node
Node* createParentMapping(Node* root, int target,
map<Node*, Node*> &nodeToParent) {
Node* res = NULL;
queue<Node*> que;
que.push(root);
nodeToParent[root] = NULL;
while (!que.empty()) {
Node* front = que.front();
if (front->data == target) {
res = front;
}
if (front->left) {
nodeToParent[front->left] = front;
que.push(front->left);
}
if (front->right) {
nodeToParent[front->right] = front;
que.push(front->right);
}
}
return res;
}
// burns the tree
int burnTree(Node* root, map<Node*, Node*> &nodeToParent) {
map<Node*, bool> visited;
queue<Node*> q;
q.push(root);
visited[root] = true;
int ans = 0;
while (!q.empty()) {
bool flag = 0;
int size = q.size();
for(int i = 0; i < size; i++) {
Node* front = q.front();
q.pop();
if (front->left && !visited[front->left]) {
flag = 1;
q.push(front->left);
visited[front->left] = 1;
}
if (front->right && !visited[front->right]) {
flag = 1;
q.push(front->right);
visited[front->right] = 1;
}
if (nodeToParent[front] && !visited[nodeToParent[front]]) {
flag = 1;
q.push(nodeToParent[front]);
visited[nodeToParent[front]] = 1;
}
}
if (flag == 1) {
ans++;
}
}
return ans;
}
public:
int minTime(Node* root, int target) {
map<Node*, Node*> NodeToParent;
Node* targetNode = createParentMapping(root, target, NodeToParent);
int time = burnTree(targetNode, NodeToParent);
return time;
}
};
I am not able to find out the where the problem lies. What am I missing?
The problem is that you have an infinite loop in createParentMapping. A call to que.pop() is missing. Just like you did in burnTree, you should put it right after the line with que.front().
This will solve your problem.
But I should mention this problem can be solved without an extra data structure. Think of how path lengths relate to heights of (sub)trees.
Here is a spoiler solution that performs one recursive traversal of the tree:
class Solution {
int answer;
// Returns:
// when negative: (negated) level at which target was found (= -(depth + 1))
// otherwise: number of levels in the tree (= height + 1)
// Side effect:
// adjusts answer
int scanTree(Node* root, int target) {
if (root == nullptr) return 0; // #levels
int left = scanTree(root->left, target);
int right = scanTree(root->right, target);
if (root->data == target) {
// found target.
answer = max(left, right); // #levels => height
return -1; // negative depth at which target was found
}
if (left < 0) { // Found the target in left subtree
answer = max(answer, right - left);
return left - 1; // Increased negative depth
} else if (right < 0 ) { // Found the target in right subtree
answer = max(answer, left - right);
return right - 1; // Increased negative depth
} else { // No success: return number of levels
return 1 + max(left, right);
}
}
public:
int minTime(Node* root, int target) {
answer = -1;
scanTree(root, target);
return answer;
}
};

How to count number times a given number appears in binary tree?

Could someone please let me know why my code isn't working? I am trying to count the number of times a given value appears in a binary tree using recursion. However, this approach is not working. I'd really appreciate some feedback and insight. Thank you.
public int valCount(int val) {
if (root == null) {
return 0;
}
return valCount(val, *root);
}
public int valCount(int val, Node *root) {
int cnt = 0;
if (root->left != null) {
if (root->data == val) {
cnt++;
}
int leftValCount = valCount(val, root->left);
}
if (root->right != null) {
if (root->data == val) {
cnt++;
}
int rightValCount = valCount(val, root->right);
}
return cnt + leftValCount + rightValCount;
}
A common mistake in recursion is to worry about stack frames other than the current one. Let the recursion do that work for you. Following this rule-of-thumb makes the code much easier to reason about.
The algorithm is simple: for each node, return 1 if the current node matches the target value else 0 if it doesn't and add the results of calling the same function on the left and right subtrees. The base case is when the function is called with a null root, return 0.
int valCount(int val, Node *root) {
if (!root) return 0;
return (root->val == val ? 1 : 0) +
valCount(val, root->left) +
valCount(val, root->right);
}

How to fix segmentation fault in AVL deletion operation when rebalancing?

I am implementing an AVL tree and my search and insertion functions work properly, but I get a segmentation fault with my remove function. I have implemented a BST tree correctly before, so I know the issue is with the rebalancing of the tree rather than the initial deletion of a node.
Since my insertion operation works with the rebalancing, I also know the issue is not with the rotation functions themselves.
I have tried different strategies such as maintaining a balance factor at each node and have tried implementing other source code I have found online but I always end up with a segmentation fault and really cannot find where. I'd appreciate any help.
class AVL
{
public:
AVL();
Node* insert(int num);
Node* search(int num);
Node* remove(int num);
void print();
void comparisonPrint();
private:
int comparisonCount;
Node* root;
int max(int a, int b);
int getHeight(Node* t);
int getBalance(Node* t);
Node* insert(Node* &t, int num);
Node* rotateWithLeftChild(Node* t);
Node* rotateWithRightChild(Node* t);
Node* doubleRotateWithLeftChild(Node* t);
Node* doubleRotateWithRightChild(Node* t);
Node* search(Node* t, int num);
Node* removeMin(Node* parent, Node* node);
Node* remove(Node* &t, int num);
void print(Node* t);
//print
};
int AVL::max(int a, int b)
{
return (a > b)? a : b;
}
int AVL::getHeight(Node* t)
{
return (t == NULL) ? 0 : t->height;
}
int AVL::getBalance(Node* t)
{
if(t == NULL)
return 0;
return getHeight(t->leftChild) - getHeight(t->rightChild);
}
//helper function for remove - finds min
Node* AVL::removeMin(Node* parent, Node* node) //removes node, but does not delete - returns ptr instead
{
if(node != NULL)
{
if(node->leftChild != NULL) //go to leftmost child in right subtree
return removeMin(node, node->leftChild);
else //min val
{
parent->leftChild = node->rightChild;
return node;
}
}
else //subtree empty - incorrect use of function
return NULL;
}
Node* AVL::remove(Node* &t, int num)
{
cout << num;
if(t != NULL)
{
if(num > t->key)
{
comparisonCount++;
remove(t->rightChild, num);
}
else if(num < t->key)
{
comparisonCount++;
remove(t->leftChild, num);
}
else if(t->leftChild != NULL && t->rightChild != NULL)
{
comparisonCount++;
//2 children
Node* minRightSubtree = removeMin(t, t->rightChild);
t->key = minRightSubtree->key;
delete minRightSubtree;
}
else
{
comparisonCount++;
//0 or 1 child
Node* temp = t;
if(t->leftChild != NULL)
t = t->leftChild;
else if(t->rightChild != NULL)
t = t->rightChild;
delete temp;
}
//update height
t->height = max(getHeight(t->leftChild), getHeight(t->rightChild)) + 1;
int balance = getBalance(t);
if(balance > 1 && getBalance(t->leftChild) >= 0)
return rotateWithRightChild(t);
if(balance > 1 && getBalance(t->leftChild) < 0)
{
t->leftChild = rotateWithLeftChild(t->leftChild);
return rotateWithRightChild(t);
}
if(balance < -1 && getBalance(t->rightChild) <= 0)
return rotateWithLeftChild(t);
if(balance < -1 && getBalance(t->rightChild) > 0)
{
t->rightChild = rotateWithRightChild(t->rightChild);
return rotateWithLeftChild(t);
}
}
return t;
}
So I need the remove function to remove a specified node and rebalance the tree when necessary using the appropriate rotations. However, I keep getting a segmentation fault whenever I try to call the function in my main. Thanks.
There are two problems with your code. First is the removeMin function and the else if part in remove function when the node to be deleted has two children.
Basic aim of the removeMin function should be to find the inorder successor of the node to be deleted which is t in your case. Consider the case when t has 2 children (both leaf nodes) then your removeMin function will set t->leftChild as t->rightChild->rightChild which is NULL which is wrong. Also the restructuring of the tree should be done inside remove hence removeMin becomes:
Node* AVL::removeMin(Node* node) // returns inorder successor of 't'
{
if(node->left == NULL)
return node;
return removeMin(node->left);
}
Coming to remove function, we reset t->key with minRightSubtree->key and the node to be deleted now is minRightSubtree. But notice that the order of keys has changed in the chain from node t till node minRightSubtree. t->key is less than all the keys of nodes till before minRightSubtree. Hence you cannot just delete minRightSubtree, you have to call remove function on the node minRightSubtree which will take care of restructuring this chain. Also you can get a little help from the recursion stack to get the correct child for the current node t after deletion/rotation:
Node* AVL::remove(Node* &t, int num)
{
if (t == NULL)
return NULL;
if (num > t->key)
t->rightChild = remove(t->rightChild, num);
else if (num < t->key)
t->leftChild = remove(t->leftChild, num);
else if (t->leftChild != NULL && t->rightChild != NULL)
{
//2 children
Node* minRightSubtree = removeMin(t->rightChild);
t->key = minRightSubtree->key;
t->rightChild = remove(t->rightChild, minRightSubtree->key);
}
else
{
//0 or 1 child
Node* temp = t;
if (t->leftChild != NULL)
t = t->leftChild;
else if (t->rightChild != NULL)
t = t->rightChild;
if(temp == t)
t = NULL;
delete temp;
}
if (t == NULL) // this case was added since there is a possibility of deleting 't'
return NULL;
//update height
t->height = max(getHeight(t->leftChild), getHeight(t->rightChild)) + 1;
int balance = getBalance(t);
if (balance > 1 && getBalance(t->leftChild) >= 0)
return rotateWithRightChild(t);
if (balance > 1 && getBalance(t->leftChild) < 0)
{
t->leftChild = rotateWithLeftChild(t->leftChild);
return rotateWithRightChild(t);
}
if (balance < -1 && getBalance(t->rightChild) <= 0)
return rotateWithLeftChild(t);
if (balance < -1 && getBalance(t->rightChild) > 0)
{
t->rightChild = rotateWithRightChild(t->rightChild);
return rotateWithLeftChild(t);
}
return t;
}
I'm assuming your code for updating heights and balancing the rooted sub-tree is correct since I've forgotten about it's theory and will need to revise.

Given a binary tree and a sum, determine if the tree has a root-to-leaf path such that adding up all the values along the path equals the given sum

So I tried my own solution in C++ but there is a bug in the code. That problem comes from judge.
So what I'm doing is keep adding a sum value and then check if the provided sum equals to the total sum in a leaf.
bool hasPathSum(TreeNode *root, int sum) {
stack<TreeNode*> st;
TreeNode *temp = root;
int SUM = 0;
bool hasSum = false;
st.push(temp);
while(!st.empty() && temp != NULL)
{
if(temp)
{
st.push(temp);
temp = temp->left;
}
else
{
st.pop();
temp = st.top();
SUM += temp->val;
if(SUM == sum)
hasSum = true;
temp = temp->right;
}
}
return hasSum;
}
Trivial to express recursively:
bool hasPathSum(TreeNode *node, int sum) {
if (!node) {
return sum == 0;
}
return hasPathSum(node->left, sum-node->val) ||
hasPathSum(node->right, sum-node->val);
}
If you translate this to a stack implementation, you will see some of the problems in yours. In particular, it is only at the leaves you want to check the sum (you check interior nodes). You have to adjust the sum as you go up and down the tree (you always add to it).
public static boolean hasPathSum(TreeNode node, int targetSum) {
if (node == null) return false;
targetSum-= node.val;
if (targetSum == 0 && node.left==null && node.right==null) {
return true;
}
int left = hasPathSum(node.left, targetSum);
int right = hasPathSum(node.right, targetSum;
return left || right;
}

How to check if a binary tree remains AVL and maintain it?

I'm having some trouble with the creation of an AVL tree structure in C++ for a university project. So far, I managed to make simple add and delete functions, but here comes the problem! I 've seen some codes here where the checks are made in the add function directly, but I didn't want to copy the code. Is there a way to create a seperate function which does exactly this work? The thing I can't make work is how to make the program understand in which rotation case I have to go.
My code so far is this:
struct node
{
unsigned int target;
struct node *left;
struct node *right;
};
int ClassInvertedIndex::Height(struct node *toCheck)
{
int left, right;
if(toCheck == NULL)
return 0;
left = Height(toCheck->left);
right = Height(toCheck->right);
if(left > right)
return left+1;
else
return right+1;
}
void ClassInvertedIndex::maintainAVL(struct node *toCheck)
{
if(Height(toCheck->left)-Height(toCheck->right) == 2); //Left subtree problem.
if(Height(toCheck->right)-Height(toCheck->left) == 2); //Right subtree problem.
}
bool ClassInvertedIndex::addNode(unsigned int x)
{
return insideAdd(data, x);
}
bool ClassInvertedIndex::insideAdd(struct node *toAdd, unsigned int x)
{
if(toAdd == NULL)
{
toAdd = new struct node;
if(toAdd == NULL)
{
cout << "Could not allocate memory.";
return false;
}
toAdd->left = NULL;
toAdd->right = NULL;
toAdd->target = x;
return true;
}
else if(x < toAdd->target)
{
bool result;
result = insideAdd(toAdd->left, x);
if(result)
{
//maintainAVL(toAdd);
}
return result;
}
else if(x > toAdd->target)
{
bool result;
result = insideAdd(toAdd->right, x);
if(result)
{
//maintainAVL(toAdd);
}
return result;
}
else //x already exists.
{
return false;
}
}
So, in the maintainAVL method, what should I do to decide what rotation I need?