Refactor BinaryTreeIsBalanced algorithm (#4222)
This commit is contained in:
parent
05ca93eace
commit
bc699b86e5
@ -586,6 +586,7 @@
|
|||||||
* [BSTRecursiveTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/trees/BSTRecursiveTest.java)
|
* [BSTRecursiveTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/trees/BSTRecursiveTest.java)
|
||||||
* [CeilInBinarySearchTreeTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/trees/CeilInBinarySearchTreeTest.java)
|
* [CeilInBinarySearchTreeTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/trees/CeilInBinarySearchTreeTest.java)
|
||||||
* [CheckBinaryTreeIsValidBSTTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/trees/CheckBinaryTreeIsValidBSTTest.java)
|
* [CheckBinaryTreeIsValidBSTTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/trees/CheckBinaryTreeIsValidBSTTest.java)
|
||||||
|
* [CheckIfBinaryTreeBalancedTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/trees/CheckIfBinaryTreeBalancedTest.java)
|
||||||
* [CheckTreeIsSymmetricTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/trees/CheckTreeIsSymmetricTest.java)
|
* [CheckTreeIsSymmetricTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/trees/CheckTreeIsSymmetricTest.java)
|
||||||
* [CreateBinaryTreeFromInorderPreorderTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/trees/CreateBinaryTreeFromInorderPreorderTest.java)
|
* [CreateBinaryTreeFromInorderPreorderTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/trees/CreateBinaryTreeFromInorderPreorderTest.java)
|
||||||
* [InorderTraversalTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/trees/InorderTraversalTest.java)
|
* [InorderTraversalTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/trees/InorderTraversalTest.java)
|
||||||
@ -687,6 +688,7 @@
|
|||||||
* [CountWordsTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/others/CountWordsTest.java)
|
* [CountWordsTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/others/CountWordsTest.java)
|
||||||
* [CRC16Test](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/others/CRC16Test.java)
|
* [CRC16Test](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/others/CRC16Test.java)
|
||||||
* [CRCAlgorithmTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/others/CRCAlgorithmTest.java)
|
* [CRCAlgorithmTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/others/CRCAlgorithmTest.java)
|
||||||
|
* [EulersFunctionTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/others/EulersFunctionTest.java)
|
||||||
* [FirstFitCPUTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/others/FirstFitCPUTest.java)
|
* [FirstFitCPUTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/others/FirstFitCPUTest.java)
|
||||||
* [KadaneAlogrithmTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/others/KadaneAlogrithmTest.java)
|
* [KadaneAlogrithmTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/others/KadaneAlogrithmTest.java)
|
||||||
* [LineSweepTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/others/LineSweepTest.java)
|
* [LineSweepTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/others/LineSweepTest.java)
|
||||||
@ -695,6 +697,7 @@
|
|||||||
* [NewManShanksPrimeTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/others/NewManShanksPrimeTest.java)
|
* [NewManShanksPrimeTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/others/NewManShanksPrimeTest.java)
|
||||||
* [NextFitTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/others/NextFitTest.java)
|
* [NextFitTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/others/NextFitTest.java)
|
||||||
* [PasswordGenTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/others/PasswordGenTest.java)
|
* [PasswordGenTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/others/PasswordGenTest.java)
|
||||||
|
* [SieveOfEratosthenesTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/others/SieveOfEratosthenesTest.java)
|
||||||
* [TestPrintMatrixInSpiralOrder](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/others/TestPrintMatrixInSpiralOrder.java)
|
* [TestPrintMatrixInSpiralOrder](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/others/TestPrintMatrixInSpiralOrder.java)
|
||||||
* [TwoPointersTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/others/TwoPointersTest.java)
|
* [TwoPointersTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/others/TwoPointersTest.java)
|
||||||
* [UniquePathsTests](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/others/UniquePathsTests.java)
|
* [UniquePathsTests](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/others/UniquePathsTests.java)
|
||||||
|
@ -5,9 +5,9 @@ import java.util.Stack;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* This class will check if a BinaryTree is balanced. A balanced binary tree is
|
* This class will check if a BinaryTree is balanced. A balanced binary tree is
|
||||||
* defined as a binary tree where the differenced in height between the left and
|
* defined as a binary tree where the difference in height between the left and
|
||||||
* right subtree of each node differs by at most one.
|
* right subtree of each node differs by at most one.
|
||||||
*
|
* <p>
|
||||||
* This can be done in both an iterative and recursive fashion. Below,
|
* This can be done in both an iterative and recursive fashion. Below,
|
||||||
* `isBalancedRecursive()` is implemented in a recursive fashion, and
|
* `isBalancedRecursive()` is implemented in a recursive fashion, and
|
||||||
* `isBalancedIterative()` is implemented in an iterative fashion.
|
* `isBalancedIterative()` is implemented in an iterative fashion.
|
||||||
@ -15,59 +15,22 @@ import java.util.Stack;
|
|||||||
* @author [Ian Cowan](https://github.com/iccowan)
|
* @author [Ian Cowan](https://github.com/iccowan)
|
||||||
*/
|
*/
|
||||||
public class CheckIfBinaryTreeBalanced {
|
public class CheckIfBinaryTreeBalanced {
|
||||||
|
|
||||||
/**
|
|
||||||
* This class implements the BinaryTree for these algorithms
|
|
||||||
*/
|
|
||||||
class BinaryTree {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The root node of the binary tree
|
|
||||||
*/
|
|
||||||
BTNode root = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class implements the nodes for the binary tree
|
|
||||||
*/
|
|
||||||
class BTNode {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The value of the node
|
|
||||||
*/
|
|
||||||
int value;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The left child of the node
|
|
||||||
*/
|
|
||||||
BTNode left = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The right child of the node
|
|
||||||
*/
|
|
||||||
BTNode right = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
*/
|
|
||||||
BTNode(int value) {
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recursive is BT balanced implementation
|
* Recursive is BT balanced implementation
|
||||||
*
|
*
|
||||||
* @param binaryTree The binary tree to check if balanced
|
* @param root The binary tree to check if balanced
|
||||||
*/
|
*/
|
||||||
public boolean isBalancedRecursive(BinaryTree binaryTree) {
|
public static boolean isBalancedRecursive(BinaryTree.Node root) {
|
||||||
|
if (root == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
// Create an array of length 1 to keep track of our balance
|
// Create an array of length 1 to keep track of our balance
|
||||||
// Default to true. We use an array so we have an efficient mutable object
|
// Default to true. We use an array, so we have an efficient mutable object
|
||||||
boolean[] isBalanced = new boolean[1];
|
boolean[] isBalanced = new boolean[1];
|
||||||
isBalanced[0] = true;
|
isBalanced[0] = true;
|
||||||
|
|
||||||
// Check for balance and return whether or not we are balanced
|
// Check for balance and return whether we are balanced
|
||||||
isBalancedRecursive(binaryTree.root, 0, isBalanced);
|
isBalancedRecursive(root, 0, isBalanced);
|
||||||
return isBalanced[0];
|
return isBalanced[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,11 +39,11 @@ public class CheckIfBinaryTreeBalanced {
|
|||||||
* recursion. We effectively perform a modified post-order traversal where
|
* recursion. We effectively perform a modified post-order traversal where
|
||||||
* we are looking at the heights of both children of each node in the tree
|
* we are looking at the heights of both children of each node in the tree
|
||||||
*
|
*
|
||||||
* @param node The current node to explore
|
* @param node The current node to explore
|
||||||
* @param depth The current depth of the node
|
* @param depth The current depth of the node
|
||||||
* @param isBalanced The array of length 1 keeping track of our balance
|
* @param isBalanced The array of length 1 keeping track of our balance
|
||||||
*/
|
*/
|
||||||
private int isBalancedRecursive(BTNode node, int depth, boolean[] isBalanced) {
|
private static int isBalancedRecursive(BinaryTree.Node node, int depth, boolean[] isBalanced) {
|
||||||
// If the node is null, we should not explore it and the height is 0
|
// If the node is null, we should not explore it and the height is 0
|
||||||
// If the tree is already not balanced, might as well stop because we
|
// If the tree is already not balanced, might as well stop because we
|
||||||
// can't make it balanced now!
|
// can't make it balanced now!
|
||||||
@ -106,22 +69,26 @@ public class CheckIfBinaryTreeBalanced {
|
|||||||
/**
|
/**
|
||||||
* Iterative is BT balanced implementation
|
* Iterative is BT balanced implementation
|
||||||
*/
|
*/
|
||||||
public boolean isBalancedIterative(BinaryTree binaryTree) {
|
public static boolean isBalancedIterative(BinaryTree.Node root) {
|
||||||
|
if (root == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Default that we are balanced and our algo will prove it wrong
|
// Default that we are balanced and our algo will prove it wrong
|
||||||
boolean isBalanced = true;
|
boolean isBalanced = true;
|
||||||
|
|
||||||
// Create a stack for our post order traversal
|
// Create a stack for our post order traversal
|
||||||
Stack<BTNode> nodeStack = new Stack<BTNode>();
|
Stack<BinaryTree.Node> nodeStack = new Stack<>();
|
||||||
|
|
||||||
// For post order traversal, we'll have to keep track of where we
|
// For post order traversal, we'll have to keep track of where we
|
||||||
// visited last
|
// visited last
|
||||||
BTNode lastVisited = null;
|
BinaryTree.Node lastVisited = null;
|
||||||
|
|
||||||
// Create a HashMap to keep track of the subtree heights for each node
|
// Create a HashMap to keep track of the subtree heights for each node
|
||||||
HashMap<BTNode, Integer> subtreeHeights = new HashMap<BTNode, Integer>();
|
HashMap<BinaryTree.Node, Integer> subtreeHeights = new HashMap<>();
|
||||||
|
|
||||||
// We begin at the root of the tree
|
// We begin at the root of the tree
|
||||||
BTNode node = binaryTree.root;
|
BinaryTree.Node node = root;
|
||||||
|
|
||||||
// We loop while:
|
// We loop while:
|
||||||
// - the node stack is empty and the node we explore is null
|
// - the node stack is empty and the node we explore is null
|
||||||
@ -165,7 +132,7 @@ public class CheckIfBinaryTreeBalanced {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// The height of the subtree containing this node is the
|
// The height of the subtree containing this node is the
|
||||||
// max of the left and right subtree heighs plus 1
|
// max of the left and right subtree heights plus 1
|
||||||
subtreeHeights.put(node, Math.max(rightHeight, leftHeight) + 1);
|
subtreeHeights.put(node, Math.max(rightHeight, leftHeight) + 1);
|
||||||
|
|
||||||
// We've now visited this node, so we pop it from the stack
|
// We've now visited this node, so we pop it from the stack
|
||||||
@ -182,86 +149,7 @@ public class CheckIfBinaryTreeBalanced {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return whether or not the tree is balanced
|
// Return whether the tree is balanced
|
||||||
return isBalanced;
|
return isBalanced;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates the following unbalanced binary tree for testing 0 / \ / \ 0 0
|
|
||||||
* / / \ / / \ 0 0 0 / \ / \ 0 0 / / 0
|
|
||||||
*/
|
|
||||||
private BinaryTree buildUnbalancedTree() {
|
|
||||||
BinaryTree tree = new BinaryTree();
|
|
||||||
tree.root = new BTNode(0);
|
|
||||||
|
|
||||||
BTNode root = tree.root;
|
|
||||||
root.left = new BTNode(0);
|
|
||||||
root.right = new BTNode(0);
|
|
||||||
|
|
||||||
BTNode left = root.left;
|
|
||||||
BTNode right = root.right;
|
|
||||||
|
|
||||||
left.left = new BTNode(0);
|
|
||||||
right.left = new BTNode(0);
|
|
||||||
right.right = new BTNode(0);
|
|
||||||
right.left.right = new BTNode(0);
|
|
||||||
|
|
||||||
left = left.left;
|
|
||||||
left.left = new BTNode(0);
|
|
||||||
left.left.left = new BTNode(0);
|
|
||||||
left.left.left.left = new BTNode(0);
|
|
||||||
|
|
||||||
return tree;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates the following balanced binary tree for testing 0 / \ / \ 0 0 /
|
|
||||||
* \ / \ / 0 / \ 0 0 0 / / / / 0 0
|
|
||||||
*/
|
|
||||||
private BinaryTree buildBalancedTree() {
|
|
||||||
BinaryTree tree = new BinaryTree();
|
|
||||||
tree.root = new BTNode(0);
|
|
||||||
|
|
||||||
BTNode root = tree.root;
|
|
||||||
root.left = new BTNode(0);
|
|
||||||
root.right = new BTNode(0);
|
|
||||||
|
|
||||||
BTNode left = root.left;
|
|
||||||
BTNode right = root.right;
|
|
||||||
|
|
||||||
left.left = new BTNode(0);
|
|
||||||
left.right = new BTNode(0);
|
|
||||||
right.left = new BTNode(0);
|
|
||||||
right.right = new BTNode(0);
|
|
||||||
|
|
||||||
right.right.left = new BTNode(0);
|
|
||||||
|
|
||||||
left.left.left = new BTNode(0);
|
|
||||||
|
|
||||||
return tree;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Main
|
|
||||||
*/
|
|
||||||
public static void main(String[] args) {
|
|
||||||
// We create a new object to check the binary trees for balance
|
|
||||||
CheckIfBinaryTreeBalanced balanceCheck = new CheckIfBinaryTreeBalanced();
|
|
||||||
|
|
||||||
// Build a balanced and unbalanced binary tree
|
|
||||||
BinaryTree balancedTree = balanceCheck.buildBalancedTree();
|
|
||||||
BinaryTree unbalancedTree = balanceCheck.buildUnbalancedTree();
|
|
||||||
|
|
||||||
// Run basic tests on the algorithms to check for balance
|
|
||||||
boolean isBalancedRB = balanceCheck.isBalancedRecursive(balancedTree); // true
|
|
||||||
boolean isBalancedRU = balanceCheck.isBalancedRecursive(unbalancedTree); // false
|
|
||||||
boolean isBalancedIB = balanceCheck.isBalancedIterative(balancedTree); // true
|
|
||||||
boolean isBalancedIU = balanceCheck.isBalancedIterative(unbalancedTree); // false
|
|
||||||
|
|
||||||
// Print the results
|
|
||||||
System.out.println("isBalancedRB: " + isBalancedRB);
|
|
||||||
System.out.println("isBalancedRU: " + isBalancedRU);
|
|
||||||
System.out.println("isBalancedIB: " + isBalancedIB);
|
|
||||||
System.out.println("isBalancedIU: " + isBalancedIU);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,84 @@
|
|||||||
|
package com.thealgorithms.datastructures.trees;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test check both implemented ways, iterative and recursive algorithms.
|
||||||
|
*
|
||||||
|
* @author Albina Gimaletdinova on 26/06/2023
|
||||||
|
*/
|
||||||
|
public class CheckIfBinaryTreeBalancedTest {
|
||||||
|
@Test
|
||||||
|
public void testRootNull() {
|
||||||
|
assertTrue(CheckIfBinaryTreeBalanced.isBalancedRecursive(null));
|
||||||
|
assertTrue(CheckIfBinaryTreeBalanced.isBalancedIterative(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOneNode() {
|
||||||
|
final BinaryTree.Node root = TreeTestUtils.createTree(new Integer[] {Integer.MIN_VALUE});
|
||||||
|
assertTrue(CheckIfBinaryTreeBalanced.isBalancedRecursive(root));
|
||||||
|
assertTrue(CheckIfBinaryTreeBalanced.isBalancedIterative(root));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
9 <-- Math.abs(height(left) - height(right)) == 0
|
||||||
|
/ \
|
||||||
|
7 13
|
||||||
|
/\ / \
|
||||||
|
3 8 10 20
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testBinaryTreeIsBalancedEqualSubtreeHeights() {
|
||||||
|
final BinaryTree.Node root = TreeTestUtils.createTree(new Integer[] {9, 7, 13, 3, 8, 10, 20});
|
||||||
|
assertTrue(CheckIfBinaryTreeBalanced.isBalancedRecursive(root));
|
||||||
|
assertTrue(CheckIfBinaryTreeBalanced.isBalancedIterative(root));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
9 <-- Math.abs(height(left) - height(right)) == 1
|
||||||
|
/ \
|
||||||
|
7 13
|
||||||
|
/\
|
||||||
|
3 8
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testBinaryTreeIsBalancedWithDifferentHeights() {
|
||||||
|
final BinaryTree.Node root = TreeTestUtils.createTree(new Integer[] {9, 7, 13, 3, 8});
|
||||||
|
assertTrue(CheckIfBinaryTreeBalanced.isBalancedRecursive(root));
|
||||||
|
assertTrue(CheckIfBinaryTreeBalanced.isBalancedIterative(root));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
9 <-- only left tree exists, Math.abs(height(left) - height(right)) > 1
|
||||||
|
/
|
||||||
|
7
|
||||||
|
/\
|
||||||
|
3 8
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testBinaryTreeNotBalanced() {
|
||||||
|
final BinaryTree.Node root = TreeTestUtils.createTree(new Integer[] {9, 7, null, 3, 8});
|
||||||
|
assertFalse(CheckIfBinaryTreeBalanced.isBalancedRecursive(root));
|
||||||
|
assertFalse(CheckIfBinaryTreeBalanced.isBalancedIterative(root));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
9 <-- Math.abs(height(left) - height(right)) > 1
|
||||||
|
/ \
|
||||||
|
7 13
|
||||||
|
/\
|
||||||
|
3 8
|
||||||
|
/
|
||||||
|
11
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testBinaryTreeNotBalancedBecauseLeftTreeNotBalanced() {
|
||||||
|
final BinaryTree.Node root = TreeTestUtils.createTree(new Integer[] {9, 7, 13, 3, 8, null, null, 11});
|
||||||
|
assertFalse(CheckIfBinaryTreeBalanced.isBalancedRecursive(root));
|
||||||
|
assertFalse(CheckIfBinaryTreeBalanced.isBalancedIterative(root));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user