スタックで 棒グラフの 中に 入る いちばん 大きい長方形を求める
幅 1 の
海外で定番の問題リスト 4 本には入っていません
問題
幅 1 のheights で
- 長方形は、
続いた 何本かの 棒に またがってかまいません。 高さは、 またがる 棒の うちいちばん低い 棒の 高さまでです heightsの長さは 1 以上 100000 以下で、 値は 0 以上 10000 以下です
入力と出力の例
例 1
2 本目と 3 本目の、
例 2
同じ
例 3
高い
面接での聞かれ方
面接では、
- 「ヒストグラムの
中の 最大の 長方形の 面積を 求めてください」 - 「O(n²) の
答えは すぐ 書けますか? そこから O(n) に できますか?」と、 段を 踏んで 詰められます - 2 次元の 0 と 1 の
表で、 1 だけで できた 最大の 長方形を 求める 問いの、 下ごしらえとして 出される こともあります
全部の
ヒント
まだ開いていません
付箋は 4 枚。下の段ほど答えに近づきます
ヒント 2
ヒント 3
ヒント 4答えに近い
アルゴリズムのチートシートの「スタックとキュー」に、
関わる学び
スタックは
まず 考える手順
段 1
素直に
段 2
言い
段 3
左から
段 4
最後まで
解答
Python
Python
def largest_rect(heights):
hs = heights + [0]
stack = []
best = 0
for i in range(len(hs)):
h = hs[i]
while stack:
top = stack[-1]
if hs[top] <= h:
break
stack.pop()
left = -1
if stack:
left = stack[-1]
w = i - left - 1
area = hs[top] * w
if area > best:
best = area
stack.append(i)
return bestJavaScript
JavaScript
function largestRect(
heights
) {
const hs = [...heights, 0];
const n = hs.length;
const stack = [];
let best = 0;
for (let i = 0; i < n; i++) {
const h = hs[i];
while (stack.length > 0) {
const top = stack.at(-1);
if (hs[top] <= h) break;
stack.pop();
const left =
stack.length > 0
? stack.at(-1)
: -1;
const w = i - left - 1;
const area = hs[top] * w;
if (area > best) {
best = area;
}
}
stack.push(i);
}
return best;
}Go
Go
func largestRect(
heights []int,
) int {
n := len(heights)
hs := make([]int, n+1)
copy(hs, heights)
stack := []int{}
best := 0
for i, h := range hs {
for len(stack) > 0 {
k := len(stack) - 1
top := stack[k]
if hs[top] <= h {
break
}
stack = stack[:k]
left := -1
if k > 0 {
left = stack[k-1]
}
w := i - left - 1
area := hs[top] * w
best = max(best, area)
}
stack = append(stack, i)
}
return best
}hs は、top のleft の 1 つ右から、i の 1 つ左までで、i - left - 1 です。heights は
計算量
- 時間は O(n) です。
どの 棒も、 積まれるのが 1 回、 取り出されるのが 1 回です - 使う
記憶は O(n) です。 高さが 上がり続ける 並びでは、 全部の 棒が スタックに 積まれます - 段 1 の
全部の 組を 試す 書き方は、 時間が O(n²) です
よく ある間違い
- 幅を
i - topで数える。 取り出した棒より 左にも、 その 棒以上の 高さの 棒が 続いていたかもしれません。 例 3 の 高さ 1 の 棒は、 取り出すとき左に もう 棒が なく、 幅は 3 です。 左の 端は スタックで 1 つ下の 棒から決めます - 最後に
高さ 0 の 棒を 足さず、 スタックに 残った 棒を 測り忘れる。 例 2 では、 3 本とも 最後まで 積まれたままで、 0 を 返してしまいます - スタックが
空に なった ときの 左の 端を 0 にする。 左に 低い棒が 無い ときの 長方形は、 並びの 左端の 棒から 始まります。 左の 端は -1 と 見て、 幅を iにします - 左の
端を、 取り出した 棒の 1 つ左の 番号にする。 取り出した 棒と 同じ 高さ以上の 棒は、 その前に もう 取り出されている ことがあります。 スタックに 残っている 棒の 番号を使います
解答を動かした版 Python 3.14.7Node.js 24.22.1Go 1.24.2確認日 2026年11月8日
面接での 話し方の例
書き始める
「いちばん
大きい長方形は、 どれかの 棒の 高さを 使っています。 棒ごとに、 左右で 最初に 当たる 低い 棒が 分かれば、 幅が 出ます。 高さが 上がる 順に 棒の 番号を スタックに 積み、 低い棒が 来たら、 上の 棒を 取り出して、 右は いまの 棒、 左は スタックで 1 つ下の 棒の あいだを 幅として 面積を 測ります。 最後に 高さ 0 の 棒を 足して 全部を 取り出します。 どの 棒も 1 回積まれて 1 回取り出されるので O(n) です。」
書き終えたら、
「例 3 で
たどります。 高さ 2 の 棒を 積みます。 高さ 1 の 棒が 来たので 2 を 取り出し、 左は 空で -1、 幅は 1 で 面積 2。 1 を 積み、 次の 2 も 積みます。 最後の 0 で、 2 を 取り出すと 左は 1 の 棒で 幅 1、 面積 2。 1 を 取り出すと 左は 空で、 幅は 3、 面積 3 です。 答えは 3 です。」