Skip to content

Commit 9af84ee

Browse files
committed
feat: add Largest Rectangle in Histogram
1 parent b61ab3d commit 9af84ee

File tree

9 files changed

+242
-3
lines changed

9 files changed

+242
-3
lines changed

.templates/leetcode/json/coin_change.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"problem_title": "Coin Change",
66
"difficulty": "Medium",
77
"topics": "Array, Dynamic Programming, Breadth-First Search",
8-
"tags": [],
8+
"tags": ["grind-75"],
99
"readme_description": "You are given an integer array `coins` representing coins of different denominations and an integer `amount` representing a total amount of money.\n\nReturn the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of the coins, return `-1`.\n\nYou may assume that you have an infinite number of each kind of coin.",
1010
"readme_examples": [
1111
{
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
{
2+
"problem_name": "largest_rectangle_in_histogram",
3+
"solution_class_name": "Solution",
4+
"problem_number": "84",
5+
"problem_title": "Largest Rectangle in Histogram",
6+
"difficulty": "Hard",
7+
"topics": "Array, Stack, Monotonic Stack",
8+
"tags": ["grind-75"],
9+
"readme_description": "Given an array of integers `heights` representing the histogram's bar height where the width of each bar is `1`, return the area of the largest rectangle in the histogram.",
10+
"readme_examples": [
11+
{
12+
"content": "![Example 1](https://assets.leetcode.com/uploads/2021/01/04/histogram.jpg)\n\n```\nInput: heights = [2,1,5,6,2,3]\nOutput: 10\n```\n**Explanation:** The above is a histogram where width of each bar is 1. The largest rectangle is shown in the red area, which has an area = 10 units."
13+
},
14+
{
15+
"content": "![Example 2](https://assets.leetcode.com/uploads/2021/01/04/histogram-1.jpg)\n\n```\nInput: heights = [2,4]\nOutput: 4\n```"
16+
}
17+
],
18+
"readme_constraints": "- `1 <= heights.length <= 10^5`\n- `0 <= heights[i] <= 10^4`",
19+
"readme_additional": "",
20+
"solution_imports": "",
21+
"solution_methods": [
22+
{
23+
"name": "largest_rectangle_area",
24+
"parameters": "heights: list[int]",
25+
"return_type": "int",
26+
"dummy_return": "0"
27+
}
28+
],
29+
"test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .solution import Solution",
30+
"test_class_name": "LargestRectangleInHistogram",
31+
"test_helper_methods": [
32+
{ "name": "setup_method", "parameters": "", "body": "self.solution = Solution()" }
33+
],
34+
"test_methods": [
35+
{
36+
"name": "test_largest_rectangle_area",
37+
"parametrize": "heights, expected",
38+
"parametrize_typed": "heights: list[int], expected: int",
39+
"test_cases": "[([2, 1, 5, 6, 2, 3], 10), ([2, 4], 4), ([1], 1), ([0], 0), ([1, 1], 2), ([0, 0, 0], 0), ([1, 2, 3, 4, 5], 9), ([5, 4, 3, 2, 1], 9), ([3, 3, 3, 3], 12), ([2, 1, 2], 3), ([1, 3, 1], 3), ([6, 7, 5, 2, 4, 5, 9, 3], 16), ([4, 2, 0, 3, 2, 5], 6), ([1, 2, 2, 1], 4), ([0, 9], 9), ([9, 0], 9), ([2, 1, 5, 6, 2, 3, 1, 5, 6, 2], 10), ([1, 8, 6, 2, 5, 4, 8, 3, 7], 16)]",
40+
"body": "result = self.solution.largest_rectangle_area(heights)\nassert result == expected"
41+
}
42+
],
43+
"playground_imports": "from solution import Solution",
44+
"playground_test_case": "# Example test case\nheights = [2, 1, 5, 6, 2, 3]\nexpected = 10",
45+
"playground_execution": "result = Solution().largest_rectangle_area(heights)\nresult",
46+
"playground_assertion": "assert result == expected"
47+
}

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 ?= coin_change
2+
PROBLEM ?= largest_rectangle_in_histogram
33
FORCE ?= 0
44
COMMA := ,
55

leetcode/coin_change/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
**Difficulty:** Medium
44
**Topics:** Array, Dynamic Programming, Breadth-First Search
5-
**Tags:**
5+
**Tags:** grind-75
66

77
**LeetCode:** [Problem 322](https://leetcode.com/problems/coin-change/description/)
88

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Largest Rectangle in Histogram
2+
3+
**Difficulty:** Hard
4+
**Topics:** Array, Stack, Monotonic Stack
5+
**Tags:** grind-75
6+
7+
**LeetCode:** [Problem 84](https://leetcode.com/problems/largest-rectangle-in-histogram/description/)
8+
9+
## Problem Description
10+
11+
Given an array of integers `heights` representing the histogram's bar height where the width of each bar is `1`, return the area of the largest rectangle in the histogram.
12+
13+
## Examples
14+
15+
### Example 1:
16+
17+
![Example 1](https://assets.leetcode.com/uploads/2021/01/04/histogram.jpg)
18+
19+
```
20+
Input: heights = [2,1,5,6,2,3]
21+
Output: 10
22+
```
23+
24+
**Explanation:** The above is a histogram where width of each bar is 1. The largest rectangle is shown in the red area, which has an area = 10 units.
25+
26+
### Example 2:
27+
28+
![Example 2](https://assets.leetcode.com/uploads/2021/01/04/histogram-1.jpg)
29+
30+
```
31+
Input: heights = [2,4]
32+
Output: 4
33+
```
34+
35+
## Constraints
36+
37+
- `1 <= heights.length <= 10^5`
38+
- `0 <= heights[i] <= 10^4`

leetcode/largest_rectangle_in_histogram/__init__.py

Whitespace-only changes.
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
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"
11+
]
12+
},
13+
{
14+
"cell_type": "code",
15+
"execution_count": 2,
16+
"id": "setup",
17+
"metadata": {},
18+
"outputs": [],
19+
"source": [
20+
"# Example test case\n",
21+
"heights = [2, 1, 5, 6, 2, 3]\n",
22+
"expected = 10"
23+
]
24+
},
25+
{
26+
"cell_type": "code",
27+
"execution_count": 3,
28+
"id": "execute",
29+
"metadata": {},
30+
"outputs": [
31+
{
32+
"data": {
33+
"text/plain": [
34+
"10"
35+
]
36+
},
37+
"execution_count": 3,
38+
"metadata": {},
39+
"output_type": "execute_result"
40+
}
41+
],
42+
"source": [
43+
"result = Solution().largest_rectangle_area(heights)\n",
44+
"result"
45+
]
46+
},
47+
{
48+
"cell_type": "code",
49+
"execution_count": 4,
50+
"id": "test",
51+
"metadata": {},
52+
"outputs": [],
53+
"source": [
54+
"assert result == expected"
55+
]
56+
}
57+
],
58+
"metadata": {
59+
"kernelspec": {
60+
"display_name": "leetcode-py-py3.13",
61+
"language": "python",
62+
"name": "python3"
63+
},
64+
"language_info": {
65+
"codemirror_mode": {
66+
"name": "ipython",
67+
"version": 3
68+
},
69+
"file_extension": ".py",
70+
"mimetype": "text/x-python",
71+
"name": "python",
72+
"nbconvert_exporter": "python",
73+
"pygments_lexer": "ipython3",
74+
"version": "3.13.7"
75+
}
76+
},
77+
"nbformat": 4,
78+
"nbformat_minor": 5
79+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
class Solution:
2+
# Time: O(n)
3+
# Space: O(n)
4+
def largest_rectangle_area(self, heights: list[int]) -> int:
5+
# Monotonic stack approach
6+
# Stack stores indices of bars in increasing height order
7+
# When we find a shorter bar, we calculate area using previous bars
8+
9+
stack: list[int] = [] # Stack of indices
10+
max_area = 0
11+
12+
for i, height in enumerate(heights):
13+
# While current height is less than stack top height
14+
# Pop from stack and calculate area with popped height as smallest
15+
while stack and heights[stack[-1]] > height:
16+
max_area = max(max_area, self.calculate_area(heights, stack, i))
17+
18+
stack.append(i)
19+
20+
while stack:
21+
max_area = max(max_area, self.calculate_area(heights, stack, len(heights)))
22+
23+
return max_area
24+
25+
@staticmethod
26+
def calculate_area(heights: list[int], stack: list[int], right_bound: int) -> int:
27+
h = heights[stack.pop()]
28+
w = right_bound if not stack else right_bound - stack[-1] - 1
29+
return h * w
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import pytest
2+
3+
from leetcode_py.test_utils import logged_test
4+
5+
from .solution import Solution
6+
7+
8+
class TestLargestRectangleInHistogram:
9+
def setup_method(self):
10+
self.solution = Solution()
11+
12+
@pytest.mark.parametrize(
13+
"heights, expected",
14+
[
15+
# Basic examples
16+
([2, 1, 5, 6, 2, 3], 10),
17+
([2, 4], 4),
18+
# Edge cases
19+
([1], 1),
20+
([0], 0),
21+
([1, 1], 2),
22+
([0, 0, 0], 0),
23+
# Patterns
24+
([1, 2, 3, 4, 5], 9),
25+
([5, 4, 3, 2, 1], 9),
26+
([3, 3, 3, 3], 12),
27+
([2, 1, 2], 3),
28+
([1, 3, 1], 3),
29+
# Complex cases
30+
([6, 7, 5, 2, 4, 5, 9, 3], 16),
31+
([4, 2, 0, 3, 2, 5], 6),
32+
([1, 2, 2, 1], 4),
33+
([0, 9], 9),
34+
([9, 0], 9),
35+
# Large rectangles
36+
([2, 1, 5, 6, 2, 3, 1, 5, 6, 2], 10),
37+
([1, 8, 6, 2, 5, 4, 8, 3, 7], 16),
38+
([50, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], 50),
39+
([1, 1, 1, 1, 1, 50, 1, 1, 1, 1, 1], 50),
40+
([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 50], 50),
41+
],
42+
)
43+
@logged_test
44+
def test_largest_rectangle_area(self, heights: list[int], expected: int):
45+
result = self.solution.largest_rectangle_area(heights)
46+
assert result == expected

0 commit comments

Comments
 (0)