Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
180 changes: 180 additions & 0 deletions 84. Largest Rectangle in Histogram.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
## step1 とりあえず解く
- 計算量
- 時間:O(N^2)
- 空間:O(N)
- Acceptされなかったコード
- 考えたこととしては、どこまでそのバーの高さを維持できるか調べる
- 高さが下がればStackから取り出して適宜最大面積を計算していく
- 最後にStackに残った要素を計算する
- このコードしか思いつかなかったが、ACできなかった理由としては
- addFirstしているため単調増加Stackを保てなかった
- 落ちるケース[3,6,5,7,4,8,1,0]
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

できればこういうときは最小のケースを書いておきたいです。
[1, 3, 2, 2] であってますか?

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

最小ケースは両端が低くて中盤に山がくる [1, 3, 2, 1] です。

- 単調増加できないとpeekLastメソッドの判定がうまくいかない

```java
class Solution {
private final record ContinuousBar(int index, int height) {};
public int largestRectangleArea(int[] heights) {
ArrayDeque<ContinuousBar> continuousBars = new ArrayDeque<>();
int maxSize = 0;
int heightsLength = heights.length;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

私はこれ変数に置かないかもしれません。(特に扱いやすくなっていないので。)まあ、置いてもいいでしょう。

for (int i = 0; i < heightsLength; i++) {
while (!continuousBars.isEmpty() && continuousBars.peekLast().height() > heights[i]) {
ContinuousBar bar = continuousBars.pollLast();
maxSize = Math.max(maxSize, (i - bar.index()) * bar.height());
continuousBars.addFirst(new ContinuousBar(bar.index(), heights[i]));
System.out.println("maxSize : " + maxSize + " " + bar.index() + " " + bar.height());
}
continuousBars.add(new ContinuousBar(i, heights[i]));
}
while (!continuousBars.isEmpty()) {
ContinuousBar bar = continuousBars.pollLast();
maxSize = Math.max(maxSize, (heightsLength - bar.index()) * bar.height());
System.out.println("maxSize2 : " + maxSize + " " + bar.index() + " : " + bar.height());
}
return maxSize;
}
}

```

## step2 他の人の回答を見る
- 計算量
- 時間:O(N)
- 空間:O(N)
- step1で自分が間違えたコードの修正版のようなコード
- 単調増加Stackを作ることで時間計算量が抑えられる
- 各要素はadd, pollが一度しかされずO(N)

```java
class Solution {

private final record RectangleCandidate(int index, int height) {};

public int largestRectangleArea(int[] heights) {
ArrayDeque<RectangleCandidate> rectangleCandidate = new ArrayDeque<>();
int maxArea = 0;

for (int i = 0; i < heights.length; i++) {
int start = i;
while (!rectangleCandidate.isEmpty() && rectangleCandidate.peekLast().height() > heights[i]) {
RectangleCandidate candidate = rectangleCandidate.pollLast();
maxArea = Math.max(maxArea, candidate.height() * (i - candidate.index()));
start = candidate.index();
}
rectangleCandidate.addLast(new RectangleCandidate(start, heights[i]));
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

heights の後ろに0を追加すると、後ろのコード不要になりそうですね。(番兵。)

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

確認ありがとうございます!
下記で対応しましたmm
面積を算出するコードが重複しており気になっていたのでリファクタできてよかったです
Step4を追加

while (!rectangleCandidate.isEmpty()) {
RectangleCandidate candidate = rectangleCandidate.pollLast();
maxArea = Math.max(maxArea, candidate.height() * (heights.length - candidate.index()));
}

return maxArea;
}
}

```

- 全探索
- 計算量
- 時間:O(N^2)
- 空間:O(1)
- i番目のバーから見て右、左にどれだけ伸ばせるかを考えるコード
- 時間計算量が大きいので今回の最適解ではないと感じた

```java
public class Solution {
public int largestRectangleArea(int[] heights) {
int n = heights.length;
int maxArea = 0;

for (int i = 0; i < n; i++) {
int height = heights[i];

int rightMost = i + 1;
while (rightMost < n && heights[rightMost] >= height) {
rightMost++;
}

int leftMost = i;
while (leftMost >= 0 && heights[leftMost] >= height) {
leftMost--;
}

rightMost--;
leftMost++;
maxArea = Math.max(maxArea, height * (rightMost - leftMost + 1));
}
return maxArea;
}
}
```

## step3 3回とく
- Step2で見たStackを使うやり方

```java
class Solution {

private final record RectangleCandidate(int index, int height) {};

public int largestRectangleArea(int[] heights) {
ArrayDeque<RectangleCandidate> rectangleCandidate = new ArrayDeque<>();
int maxArea = 0;
for (int i = 0; i < heights.length; i++) {
int startIndex = i;
while (!rectangleCandidate.isEmpty() && rectangleCandidate.peekLast().height() > heights[i]) {
RectangleCandidate candidate = rectangleCandidate.pollLast();
maxArea = Math.max(maxArea, candidate.height() * (i - candidate.index()));
startIndex = candidate.index();
}
rectangleCandidate.add(new RectangleCandidate(startIndex, heights[i]));
}
while (!rectangleCandidate.isEmpty()) {
RectangleCandidate candidate = rectangleCandidate.pollLast();
maxArea = Math.max(maxArea, candidate.height() * (heights.length - candidate.index()));
}

return maxArea;
}
}

```


## step4 いただいた指摘を元に修正
>heights の後ろに0を追加すると、後ろのコード不要になりそうですね。(番兵。)

- 上記コメントを元に修正
- `System.arraycopy` を用いて配列コピーして最後に0を追加してもよかったが、配列コピーのためにメモリがO(N)追加で必要になるため採用しなかった

```java
class Solution {

private final record RectangleCandidate(int index, int height) {};

public int largestRectangleArea(int[] heights) {
ArrayDeque<RectangleCandidate> rectangleCandidate = new ArrayDeque<>();
int maxArea = 0;
for (int i = 0; i <= heights.length; i++) {
int startIndex = i;
int currentHeight;
if (i == heights.length) {
currentHeight = 0;
} else {
currentHeight = heights[i];
}
while (!rectangleCandidate.isEmpty() && rectangleCandidate.peekLast().height() > currentHeight) {
RectangleCandidate candidate = rectangleCandidate.pollLast();
maxArea = Math.max(maxArea, candidate.height() * (i - candidate.index()));
startIndex = candidate.index();
}
rectangleCandidate.add(new RectangleCandidate(startIndex, currentHeight));
}

return maxArea;
}
}

```