# Largest Rectangle in Histogram

### Clarify the problem

Before we begin, let's clarify the problem statement. The "Largest Rectangle in Histogram" problem asks us to find the area of the largest rectangle that can be formed within a given histogram. The histogram consists of a series of non-negative integers, where each integer represents the height of a bar. The width of each bar is assumed to be 1. Our task is to find the maximum area of a rectangle that can be formed using the bars of the histogram.

### Analyze the problem

Let's analyze the problem to identify the input, output, and constraints.

**Input:** An array of non-negative integers representing the heights of bars in a histogram.

**Output:** The integer value representing the area of the largest rectangle that can be formed within the histogram.

**Constraints:**

- The input array can have up to 10^4 elements.
- Each element in the array is a non-negative integer.

### Design an algorithm

To solve this problem efficiently, we can use a stack-based approach. Here's the general outline of our algorithm:

- Initialize an empty stack to store the indices of the histogram bars.
- Iterate through each bar in the histogram.
- If the stack is empty or the current bar's height is greater than or equal to the bar at the top of the stack, push the current bar's index onto the stack.
- Otherwise, keep popping bars from the stack until we find a bar with a height smaller than the current bar. For each popped bar, calculate the area of the rectangle it can form.

- After iterating through all the bars, check if the stack is empty. If not, repeat step 2 for the remaining bars in the stack to calculate the area of the rectangles they can form.
- Keep track of the maximum area encountered during the process and return it as the result.

### Explain your approach

Our approach involves using a stack to keep track of the indices of the histogram bars in non-decreasing order of heights. By maintaining this order, we can easily calculate the area of the rectangles formed by bars as we iterate through the histogram.

### Write clean and readable code

Let's implement the algorithm in Python:

```
def largestRectangleArea(heights):
stack = []
max_area = 0
index = 0
while index < len(heights):
if not stack or heights[index] >= heights[stack[-1]]:
stack.append(index)
index += 1
else:
top = stack.pop()
area = heights[top] * (index - stack[-1] - 1 if stack else index)
max_area = max(max_area, area)
while stack:
top = stack.pop()
area = heights[top] * (index - stack[-1] - 1 if stack else index)
max_area = max(max_area, area)
return max_area
```

### Test your code

Let's test the code with some example test cases and explain why we chose them:

```
# Example test cases
print(largestRectangleArea([2, 1, 5, 6, 2, 3])) # Expected: 10
print(largestRectangleArea([1, 2, 3, 4, 5])) # Expected: 9
print(largestRectangleArea([5, 4, 3, 2, 1])) # Expected: 9
```

We chose these test cases to cover different scenarios:

- The first test case has a peak in the middle of the histogram, testing if the algorithm can correctly calculate the area in such cases.
- The second and third test cases have histograms in ascending and descending order, respectively, to ensure the algorithm handles these scenarios correctly.

### Optimize if necessary

The algorithm we implemented has a time complexity of O(n), where n is the number of elements in the input array. This is because we iterate through the array once and perform constant-time operations for each element.

The space complexity of the algorithm is O(n) as well since the stack can potentially store all the elements of the input array.

Thus, the algorithm is already efficient and doesn't require further optimization.

### Handle error cases

Our code assumes that the input will be a valid histogram represented by a non-empty list of non-negative integers. However, it doesn't handle cases where the input is empty or not a list.

To handle these error cases gracefully, we can add some input validation at the beginning of our code:

```
def largestRectangleArea(heights):
if not isinstance(heights, list) or len(heights) == 0:
return 0
# Rest of the code
return max_area
```

This modification ensures that the function returns 0 for empty or invalid inputs.

### Discuss complexity analysis

As mentioned earlier, the time complexity of the algorithm is O(n), where n is the number of elements in the input array. This is because we iterate through the array once.

The space complexity is also O(n) since the stack can store up to n elements in the worst case scenario.

The algorithm's performance is optimal, and it can handle large inputs efficiently without exceeding memory limitations.