-
Notifications
You must be signed in to change notification settings - Fork 0
【Grind75Hard】9問目84. Largest Rectangle in Histogram #68
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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)) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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]]) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. stack[-1][1] == heights[end]
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 同値はここで処理しなくても、次の |
||
| while stack: | ||
| start, height = stack.pop() | ||
| max_area = max(max_area, height * (len(heights) - start)) | ||
| return max_area | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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: | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 番兵を使う、すなわち、stack = [] の前に、heights.append(0) という手があるかもしれません。
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 番兵を使った方が最後の後処理がなくなって分かりやすいです。 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 一応、入力を破壊的に変更すると、呼んだ人がびっくりする可能性があるので、それを指摘されたら考えましょう。
マルチスレッドで起きる諸々といえば、
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 1,2の選択肢は盲点でした。 The Deadlock Empireとても分かりやすいですね。 |
||
| start, height = stack.pop() | ||
| area = height * (len(heights) - start) | ||
| max_area = max(max_area, area) | ||
| return max_area | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return min_value_index
でもいいですか?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
問題ないです。無駄に分けてました。
return min_value_indexの方がシンプルで分かりやすいです。