Skip to content

Commit 8f83609

Browse files
committed
feat: add Balanced Binary Tree
1 parent bd6d987 commit 8f83609

File tree

7 files changed

+318
-1
lines changed

7 files changed

+318
-1
lines changed
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
{
2+
"problem_name": "balanced_binary_tree",
3+
"solution_class_name": "Solution",
4+
"problem_number": "110",
5+
"problem_title": "Balanced Binary Tree",
6+
"difficulty": "Easy",
7+
"topics": "Tree, Depth-First Search, Binary Tree",
8+
"tags": ["grind-75"],
9+
"readme_description": "Given a binary tree, determine if it is **height-balanced**.\n\nA height-balanced binary tree is a binary tree in which the depth of the two subtrees of every node never differs by more than one.",
10+
"readme_examples": [
11+
{
12+
"content": "![Example 1](https://assets.leetcode.com/uploads/2020/10/06/balance_1.jpg)\n\n```\nInput: root = [3,9,20,null,null,15,7]\nOutput: true\n```"
13+
},
14+
{
15+
"content": "![Example 2](https://assets.leetcode.com/uploads/2020/10/06/balance_2.jpg)\n\n```\nInput: root = [1,2,2,3,3,null,null,4,4]\nOutput: false\n```"
16+
},
17+
{ "content": "```\nInput: root = []\nOutput: true\n```" }
18+
],
19+
"readme_constraints": "- The number of nodes in the tree is in the range `[0, 5000]`.\n- `-10^4 <= Node.val <= 10^4`",
20+
"readme_additional": "",
21+
"solution_imports": "from leetcode_py import TreeNode",
22+
"solution_methods": [
23+
{
24+
"name": "is_balanced",
25+
"parameters": "root: TreeNode | None",
26+
"return_type": "bool",
27+
"dummy_return": "False"
28+
}
29+
],
30+
"test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom leetcode_py import TreeNode\nfrom .solution import Solution",
31+
"test_class_name": "BalancedBinaryTree",
32+
"test_helper_methods": [
33+
{ "name": "setup_method", "parameters": "", "body": "self.solution = Solution()" }
34+
],
35+
"test_methods": [
36+
{
37+
"name": "test_is_balanced",
38+
"parametrize": "root_list, expected",
39+
"parametrize_typed": "root_list: list[int | None], expected: bool",
40+
"test_cases": "[([3, 9, 20, None, None, 15, 7], True), ([1, 2, 2, 3, 3, None, None, 4, 4], False), ([], True)]",
41+
"body": "root = TreeNode.from_list(root_list)\nresult = self.solution.is_balanced(root)\nassert result == expected"
42+
}
43+
],
44+
"playground_imports": "from solution import Solution\nfrom leetcode_py import TreeNode",
45+
"playground_test_case": "# Example test case\nroot_list = [3, 9, 20, None, None, 15, 7]\nroot = TreeNode.from_list(root_list)\nexpected = True",
46+
"playground_execution": "result = Solution().is_balanced(root)\nresult",
47+
"playground_assertion": "assert result == expected"
48+
}

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
PYTHON_VERSION = 3.13
2-
PROBLEM ?= valid_parentheses
2+
PROBLEM ?= balanced_binary_tree
33
FORCE ?= 0
44
COMMA := ,
55

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Balanced Binary Tree
2+
3+
**Difficulty:** Easy
4+
**Topics:** Tree, Depth-First Search, Binary Tree
5+
**Tags:** grind-75
6+
7+
**LeetCode:** [Problem 110](https://leetcode.com/problems/balanced-binary-tree/description/)
8+
9+
## Problem Description
10+
11+
Given a binary tree, determine if it is **height-balanced**.
12+
13+
A height-balanced binary tree is a binary tree in which the depth of the two subtrees of every node never differs by more than one.
14+
15+
## Examples
16+
17+
### Example 1:
18+
19+
![Example 1](https://assets.leetcode.com/uploads/2020/10/06/balance_1.jpg)
20+
21+
```
22+
Input: root = [3,9,20,null,null,15,7]
23+
Output: true
24+
```
25+
26+
### Example 2:
27+
28+
![Example 2](https://assets.leetcode.com/uploads/2020/10/06/balance_2.jpg)
29+
30+
```
31+
Input: root = [1,2,2,3,3,null,null,4,4]
32+
Output: false
33+
```
34+
35+
### Example 3:
36+
37+
```
38+
Input: root = []
39+
Output: true
40+
```
41+
42+
## Constraints
43+
44+
- The number of nodes in the tree is in the range `[0, 5000]`.
45+
- `-10^4 <= Node.val <= 10^4`

leetcode/balanced_binary_tree/__init__.py

Whitespace-only changes.
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "code",
5+
"execution_count": 1,
6+
"id": "imports",
7+
"metadata": {},
8+
"outputs": [],
9+
"source": [
10+
"from solution import Solution\n",
11+
"\n",
12+
"from leetcode_py import TreeNode"
13+
]
14+
},
15+
{
16+
"cell_type": "code",
17+
"execution_count": 2,
18+
"id": "setup",
19+
"metadata": {},
20+
"outputs": [],
21+
"source": [
22+
"# Example test case\n",
23+
"root_list = [3, 9, 20, None, None, 15, 7]\n",
24+
"root = TreeNode.from_list(root_list)\n",
25+
"expected = True"
26+
]
27+
},
28+
{
29+
"cell_type": "code",
30+
"execution_count": 3,
31+
"id": "f76011d0",
32+
"metadata": {},
33+
"outputs": [
34+
{
35+
"data": {
36+
"text/html": [
37+
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n",
38+
"<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n",
39+
" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n",
40+
"<!-- Generated by graphviz version 13.1.2 (20250808.2320)\n",
41+
" -->\n",
42+
"<!-- Pages: 1 -->\n",
43+
"<svg width=\"170pt\" height=\"188pt\"\n",
44+
" viewBox=\"0.00 0.00 170.00 188.00\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n",
45+
"<g id=\"graph0\" class=\"graph\" transform=\"scale(1 1) rotate(0) translate(4 184)\">\n",
46+
"<polygon fill=\"white\" stroke=\"none\" points=\"-4,4 -4,-184 166,-184 166,4 -4,4\"/>\n",
47+
"<!-- 0 -->\n",
48+
"<g id=\"node1\" class=\"node\">\n",
49+
"<title>0</title>\n",
50+
"<ellipse fill=\"none\" stroke=\"black\" cx=\"63\" cy=\"-162\" rx=\"27\" ry=\"18\"/>\n",
51+
"<text xml:space=\"preserve\" text-anchor=\"middle\" x=\"63\" y=\"-156.95\" font-family=\"Times,serif\" font-size=\"14.00\">3</text>\n",
52+
"</g>\n",
53+
"<!-- 1 -->\n",
54+
"<g id=\"node2\" class=\"node\">\n",
55+
"<title>1</title>\n",
56+
"<ellipse fill=\"none\" stroke=\"black\" cx=\"27\" cy=\"-90\" rx=\"27\" ry=\"18\"/>\n",
57+
"<text xml:space=\"preserve\" text-anchor=\"middle\" x=\"27\" y=\"-84.95\" font-family=\"Times,serif\" font-size=\"14.00\">9</text>\n",
58+
"</g>\n",
59+
"<!-- 0&#45;&gt;1 -->\n",
60+
"<g id=\"edge1\" class=\"edge\">\n",
61+
"<title>0&#45;&gt;1</title>\n",
62+
"<path fill=\"none\" stroke=\"black\" d=\"M54.65,-144.76C50.42,-136.55 45.19,-126.37 40.42,-117.09\"/>\n",
63+
"<polygon fill=\"black\" stroke=\"black\" points=\"43.68,-115.79 36,-108.49 37.46,-118.99 43.68,-115.79\"/>\n",
64+
"</g>\n",
65+
"<!-- 2 -->\n",
66+
"<g id=\"node3\" class=\"node\">\n",
67+
"<title>2</title>\n",
68+
"<ellipse fill=\"none\" stroke=\"black\" cx=\"99\" cy=\"-90\" rx=\"27\" ry=\"18\"/>\n",
69+
"<text xml:space=\"preserve\" text-anchor=\"middle\" x=\"99\" y=\"-84.95\" font-family=\"Times,serif\" font-size=\"14.00\">20</text>\n",
70+
"</g>\n",
71+
"<!-- 0&#45;&gt;2 -->\n",
72+
"<g id=\"edge2\" class=\"edge\">\n",
73+
"<title>0&#45;&gt;2</title>\n",
74+
"<path fill=\"none\" stroke=\"black\" d=\"M71.35,-144.76C75.58,-136.55 80.81,-126.37 85.58,-117.09\"/>\n",
75+
"<polygon fill=\"black\" stroke=\"black\" points=\"88.54,-118.99 90,-108.49 82.32,-115.79 88.54,-118.99\"/>\n",
76+
"</g>\n",
77+
"<!-- 3 -->\n",
78+
"<g id=\"node4\" class=\"node\">\n",
79+
"<title>3</title>\n",
80+
"<ellipse fill=\"none\" stroke=\"black\" cx=\"63\" cy=\"-18\" rx=\"27\" ry=\"18\"/>\n",
81+
"<text xml:space=\"preserve\" text-anchor=\"middle\" x=\"63\" y=\"-12.95\" font-family=\"Times,serif\" font-size=\"14.00\">15</text>\n",
82+
"</g>\n",
83+
"<!-- 2&#45;&gt;3 -->\n",
84+
"<g id=\"edge3\" class=\"edge\">\n",
85+
"<title>2&#45;&gt;3</title>\n",
86+
"<path fill=\"none\" stroke=\"black\" d=\"M90.65,-72.76C86.42,-64.55 81.19,-54.37 76.42,-45.09\"/>\n",
87+
"<polygon fill=\"black\" stroke=\"black\" points=\"79.68,-43.79 72,-36.49 73.46,-46.99 79.68,-43.79\"/>\n",
88+
"</g>\n",
89+
"<!-- 4 -->\n",
90+
"<g id=\"node5\" class=\"node\">\n",
91+
"<title>4</title>\n",
92+
"<ellipse fill=\"none\" stroke=\"black\" cx=\"135\" cy=\"-18\" rx=\"27\" ry=\"18\"/>\n",
93+
"<text xml:space=\"preserve\" text-anchor=\"middle\" x=\"135\" y=\"-12.95\" font-family=\"Times,serif\" font-size=\"14.00\">7</text>\n",
94+
"</g>\n",
95+
"<!-- 2&#45;&gt;4 -->\n",
96+
"<g id=\"edge4\" class=\"edge\">\n",
97+
"<title>2&#45;&gt;4</title>\n",
98+
"<path fill=\"none\" stroke=\"black\" d=\"M107.35,-72.76C111.58,-64.55 116.81,-54.37 121.58,-45.09\"/>\n",
99+
"<polygon fill=\"black\" stroke=\"black\" points=\"124.54,-46.99 126,-36.49 118.32,-43.79 124.54,-46.99\"/>\n",
100+
"</g>\n",
101+
"</g>\n",
102+
"</svg>\n"
103+
],
104+
"text/plain": [
105+
"TreeNode([3, 9, 20, None, None, 15, 7])"
106+
]
107+
},
108+
"execution_count": 3,
109+
"metadata": {},
110+
"output_type": "execute_result"
111+
}
112+
],
113+
"source": [
114+
"root"
115+
]
116+
},
117+
{
118+
"cell_type": "code",
119+
"execution_count": 4,
120+
"id": "execute",
121+
"metadata": {},
122+
"outputs": [
123+
{
124+
"data": {
125+
"text/plain": [
126+
"True"
127+
]
128+
},
129+
"execution_count": 4,
130+
"metadata": {},
131+
"output_type": "execute_result"
132+
}
133+
],
134+
"source": [
135+
"result = Solution().is_balanced(root)\n",
136+
"result"
137+
]
138+
},
139+
{
140+
"cell_type": "code",
141+
"execution_count": 5,
142+
"id": "test",
143+
"metadata": {},
144+
"outputs": [],
145+
"source": [
146+
"assert result == expected"
147+
]
148+
}
149+
],
150+
"metadata": {
151+
"kernelspec": {
152+
"display_name": "leetcode-py-py3.13",
153+
"language": "python",
154+
"name": "python3"
155+
},
156+
"language_info": {
157+
"codemirror_mode": {
158+
"name": "ipython",
159+
"version": 3
160+
},
161+
"file_extension": ".py",
162+
"mimetype": "text/x-python",
163+
"name": "python",
164+
"nbconvert_exporter": "python",
165+
"pygments_lexer": "ipython3",
166+
"version": "3.13.7"
167+
}
168+
},
169+
"nbformat": 4,
170+
"nbformat_minor": 5
171+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from leetcode_py import TreeNode
2+
3+
4+
class Solution:
5+
# Time: O(n)
6+
# Space: O(h)
7+
def is_balanced(self, root: TreeNode | None) -> bool:
8+
def height(node: TreeNode | None) -> int:
9+
if not node:
10+
return 0
11+
12+
left = height(node.left)
13+
right = height(node.right)
14+
15+
if left == -1 or right == -1 or abs(left - right) > 1:
16+
return -1
17+
18+
return max(left, right) + 1
19+
20+
return height(root) != -1
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import pytest
2+
3+
from leetcode_py import TreeNode
4+
from leetcode_py.test_utils import logged_test
5+
6+
from .solution import Solution
7+
8+
9+
class TestBalancedBinaryTree:
10+
def setup_method(self):
11+
self.solution = Solution()
12+
13+
@pytest.mark.parametrize(
14+
"root_list, expected",
15+
[
16+
([3, 9, 20, None, None, 15, 7], True),
17+
([1, 2, 2, 3, 3, None, None, 4, 4], False),
18+
([], True),
19+
([1], True),
20+
([1, 2], True),
21+
([1, None, 2], True),
22+
([1, 2, 3], True),
23+
([1, 2, None, 3], False),
24+
([1, None, 2, None, 3], False),
25+
([1, 2, 3, 4, 5, 6, 7], True),
26+
([1, 2, 3, 4, None, None, 7, 8], False),
27+
],
28+
)
29+
@logged_test
30+
def test_is_balanced(self, root_list: list[int | None], expected: bool):
31+
root = TreeNode.from_list(root_list)
32+
result = self.solution.is_balanced(root)
33+
assert result == expected

0 commit comments

Comments
 (0)