diff --git a/grind75_hard/09_84_Largest Rectangle in Histogram/level_1.py b/grind75_hard/09_84_Largest Rectangle in Histogram/level_1.py new file mode 100644 index 0000000..d67abb7 --- /dev/null +++ b/grind75_hard/09_84_Largest Rectangle in Histogram/level_1.py @@ -0,0 +1,91 @@ +# 総当たり(TLE) +# O(N^3) +class Solution: + def largestRectangleArea(self, heights: List[int]) -> int: + max_rectangle = [0] * len(heights) + for i in range(len(heights)): + for j in range(i + 1, len(heights) + 1): + rectangle = min(heights[i:j]) * (j - i) + max_rectangle[i] = max(max_rectangle[i], rectangle) + return max(max_rectangle) + + +# 最小値の判定を効率化した総当たり(TLE) +# O(N^2) +class Solution: + def largestRectangleArea(self, heights: List[int]) -> int: + max_rectangle = [0] * len(heights) + for i in range(len(heights)): + min_height = heights[i] + for j in range(i, len(heights)): + min_height = min(min_height, heights[j]) + rectangle = min_height * (j - i + 1) + max_rectangle[i] = max(max_rectangle[i], rectangle) + return max(max_rectangle) + + +# 最小値で左右に分割してそれぞれを比較(TLE) +# O(NlogN) +# ソートされている時はO(N^2) +class Solution: + def largestRectangleArea(self, heights: List[int]) -> int: + def calc_area(left, right): + if left >= right: + return 0 + min_value = float("inf") + for i in range(left, right): + if min_value < heights[i]: + continue + min_value = heights[i] + min_index = i + area = min_value * (right - left) + max_area = max( + area, calc_area(left, min_index), calc_area(min_index + 1, right) + ) + return max_area + + return calc_area(0, len(heights)) + + +# 最小値をsegment treeで管理して効率化 +# O(NlogN) +class SegmentTree: + def __init__(self, nums): + self.n = len(nums) + self.tree = [None] * (2 * self.n) + for i, num in enumerate(nums): + self.tree[self.n + i] = (num, i) + for i in range(self.n - 1, 0, -1): + self.tree[i] = min(self.tree[i * 2], self.tree[i * 2 + 1]) + + def query(self, left, right): + left += self.n + right += self.n + min_value_index = (float("inf"), 0) + while left < right: + if left % 2 == 1: + min_value_index = min(min_value_index, self.tree[left]) + left += 1 + if right % 2 == 1: + right -= 1 + min_value_index = min(min_value_index, self.tree[right]) + left //= 2 + right //= 2 + return min_value_index[0], min_value_index[1] + + +class Solution: + def largestRectangleArea(self, heights: List[int]) -> int: + segment_tree = SegmentTree(heights) + + def calc_area(left, right): + if left >= right: + return 0 + min_value, min_index = segment_tree.query(left, right) + area = min_value * (right - left) + max_area = max( + area, calc_area(left, min_index), calc_area(min_index + 1, right) + ) + return max_area + + return calc_area(0, len(heights)) diff --git a/grind75_hard/09_84_Largest Rectangle in Histogram/level_2.py b/grind75_hard/09_84_Largest Rectangle in Histogram/level_2.py new file mode 100644 index 0000000..02f45a6 --- /dev/null +++ b/grind75_hard/09_84_Largest Rectangle in Histogram/level_2.py @@ -0,0 +1,21 @@ +# 左から高さを確認していき、高さが減少するタイミングでその高さより高い場合の面積を計算する。 +# [3, 5, 2, 6]の場合、2に到達した時に高さが減少するので、それより左側にある2より高い3と5の面積を計算する。 +# 5のとき: 5 * 1 = 5 +# 3のとき: 3 * 2 = 6 +# 端まで到達したら、計算されなかった要素で計算する。 +# 2のとき: 2 * 4 = 6 +# 6のとき: 6 * 1 = 6 +class Solution: + def largestRectangleArea(self, heights: List[int]) -> int: + max_area = 0 + stack = [] + for end in range(len(heights)): + start = end + while stack and stack[-1][1] > heights[end]: + start, height = stack.pop() + max_area = max(max_area, height * (end - start)) + stack.append([start, heights[end]]) + while stack: + start, height = stack.pop() + max_area = max(max_area, height * (len(heights) - start)) + return max_area diff --git a/grind75_hard/09_84_Largest Rectangle in Histogram/level_3.py b/grind75_hard/09_84_Largest Rectangle in Histogram/level_3.py new file mode 100644 index 0000000..55a8f1c --- /dev/null +++ b/grind75_hard/09_84_Largest Rectangle in Histogram/level_3.py @@ -0,0 +1,16 @@ +class Solution: + def largestRectangleArea(self, heights: List[int]) -> int: + max_area = 0 + stack = [] + for end in range(len(heights)): + start = end + while stack and stack[-1][1] > heights[end]: + start, height = stack.pop() + area = height * (end - start) + max_area = max(max_area, area) + stack.append((start, heights[end])) + while stack: + start, height = stack.pop() + area = height * (len(heights) - start) + max_area = max(max_area, area) + return max_area diff --git a/grind75_hard/09_84_Largest Rectangle in Histogram/level_4.py b/grind75_hard/09_84_Largest Rectangle in Histogram/level_4.py new file mode 100644 index 0000000..600b847 --- /dev/null +++ b/grind75_hard/09_84_Largest Rectangle in Histogram/level_4.py @@ -0,0 +1,63 @@ +# segment treeを書き直した +# rootのindexは1 +# 親ノード: i // 2 +# 子ノード: (i * 2), (i * 2 + 1) +class SegmentTree: + def __init__(self, nums): + self.size = 1 + while self.size < len(nums): + self.size *= 2 + self.tree = [(float("inf"), 0)] * (self.size * 2) + # 葉ノードに値をセット + for i, num in enumerate(nums): + self.tree[self.size + i] = (num, i) + # 葉ノード以外に最小値をセット + for i in range(self.size - 1, 0, -1): + self.tree[i] = min(self.tree[i * 2], self.tree[i * 2 + 1]) + + # 最上段から下がっていき、[begin, end)の最小値を取得 + # [node_begin, node_end)が現在のノードの区間 + def query(self, begin, end, node=1, node_begin=0, node_end=-1): + if node_end == -1: + node_end = self.size + if node_end <= begin or end <= node_begin: # 対象区間が被らない + return (float("inf"), 0) + if begin <= node_begin and node_end <= end: # 対象区間が完全に被る + return self.tree[node] + # 一部だけ被る -> 子ノードに問い合わせ + node_middle = (node_begin + node_end) // 2 + left_min = self.query(begin, end, node * 2, node_begin, node_middle) + right_min = self.query(begin, end, node * 2 + 1, node_middle, node_end) + return min(left_min, right_min) + + +class Solution: + def largestRectangleArea(self, heights: List[int]) -> int: + segment_tree = SegmentTree(heights) + + def calc_area(begin, end): + if begin >= end: + return 0 + min_value, min_index = segment_tree.query(begin, end) + area = min_value * (end - begin) + max_area = max( + area, calc_area(begin, min_index), calc_area(min_index + 1, end) + ) + return max_area + + return calc_area(0, len(heights)) + + +# stackを使った解法に番兵を利用 +class Solution: + def largestRectangleArea(self, heights: List[int]) -> int: + max_area = 0 + heights.append(0) + stack = [] + for end in range(len(heights)): + start = end + while stack and stack[-1][1] >= heights[end]: + start, height = stack.pop() + max_area = max(max_area, height * (end - start)) + stack.append([start, heights[end]]) + return max_area