ヒープで いちばん重い 2 つを 打ち合わせて 最後に 残る 重さを求める
金属の
海外で定番の問題リスト 4 本には入っていません
問題
金属のweights を
- 残っている
玉の うち、 いちばん重い 玉と いちばん重い 次の 玉を 1 つずつ 取り出して 打ち合わせます - 2 つの
重さが 同じなら、 2 つとも 砕けて消えます - 重さが
違うなら、 軽い方は 消え、 重い方は 重さが 「2 つの差」に なって 機械に戻ります
玉が 1 つ以下に
weightsの長さは 1 以上 1000 以下、 重さは どれも 1 以上 1000 以下の整数です - 毎回並べ替える
書き方ではなく、 ヒープを 使って O(n log n) で 求めてください
入力と出力の例
例 1
8 と 6 を
例 2
5 と 5 は
例 3
玉が 1 つだけなので、
面接での聞かれ方
面接では、
- 「石の
重さの 並びが あります。 いちばん重い 2 つを ぶつけ合う ことを 繰り返して、 最後に 残る 重さを 返してください」 - 書き終えると
「毎回並べ替えるのと 比べて、 どれだけ 速いですか?」と 聞かれます - Python で
書くと 「heapq は 小さい 順なのに、 どうやって 大きい順に 使いましたか?」と、 符号を 返す工夫の 理由を 聞かれます
差が 0 の
ヒント
まだ開いていません
付箋は 4 枚。下の段ほど答えに近づきます
ヒント 2
ヒント 3
ヒント 4答えに近い
アルゴリズムのチートシートの「スタックとキュー」に、
関わる学び
スタックは
まず 考える手順
段 1
書いてある
段 2
欲しいのは、
段 3
ヒープから 2 回取り出し、
段 4
Python の heapq は- をcontainer/heap の Less を > に
解答
Python
Python
from heapq import heapify
from heapq import heappop
from heapq import heappush
def last_stone(weights):
h = [-w for w in weights]
heapify(h)
while len(h) > 1:
a = -heappop(h)
b = -heappop(h)
if a != b:
heappush(h, b - a)
return -h[0] if h else 0JavaScript
JavaScript
function lastStone(weights) {
const h = [];
for (const w of weights) {
push(h, w);
}
while (h.length > 1) {
const a = pop(h);
const b = pop(h);
if (a !== b) {
push(h, a - b);
}
}
return h.length ? h[0] : 0;
}
function push(h, x) {
h.push(x);
let i = h.length - 1;
while (i > 0) {
const p = (i - 1) >> 1;
if (h[p] >= h[i]) {
break;
}
[h[p], h[i]] = [h[i], h[p]];
i = p;
}
}
function pop(h) {
const top = h[0];
const last = h.pop();
if (h.length === 0) {
return top;
}
h[0] = last;
let i = 0;
while (true) {
let s = i;
const l = 2 * i + 1;
const r = l + 1;
const n = h.length;
if (l < n && h[l] > h[s]) {
s = l;
}
if (r < n && h[r] > h[s]) {
s = r;
}
if (s === i) {
return top;
}
[h[s], h[i]] = [h[i], h[s]];
i = s;
}
}Go
Go
import "container/heap"
type maxHeap []int
func (h maxHeap) Len() int {
return len(h)
}
func (h maxHeap) Less(
i, j int,
) bool {
return h[i] > h[j]
}
func (h maxHeap) Swap(
i, j int,
) {
h[i], h[j] = h[j], h[i]
}
func (h *maxHeap) Push(x any) {
*h = append(*h, x.(int))
}
func (h *maxHeap) Pop() any {
old := *h
x := old[len(old)-1]
*h = old[:len(old)-1]
return x
}
func lastStone(
weights []int,
) int {
h := maxHeap(append(
[]int{}, weights...))
heap.Init(&h)
for h.Len() > 1 {
a := heap.Pop(&h).(int)
b := heap.Pop(&h).(int)
if a != b {
heap.Push(&h, a-b)
}
}
if h.Len() == 0 {
return 0
}
return h[0]
}Go は weights をmaxHeap(weights) のままだと、heapify と Go の heap.Init は、
計算量
- 時間は O(n log n) です。
打ち合わせは n 回より 少なく、 1 回ごとに 取り出し 2 回と 足す 1 回が、 どれも O(log n) です - 使う
記憶は O(n) です。 ヒープに 玉を 全部持ちます
よく ある間違い
- 差が 0 の
玉を戻す。 重さ 0 の 玉を 戻すと、 例 2 で 0 が 1 つ 残り、 たまたま 答えは 合っても、 玉の 数の 数え方が ずれます。 差が 0 の ときは 何も 戻しません - Python で
符号を 戻し忘れる。 -heappop(h)の-を片方だけ 忘れると、 差の 計算が 狂います。 入れる 所と 出す所の 両方で 符号を付けます - 空の
ヒープの 根を見る。 最後に 玉が 残らない とき h[0]を見ると、 Python は 例外、 Go は 範囲外で 止まります。 先に 数を 確かめます - JavaScript で
毎回 sortする。 1 回で O(n log n) かかり、全体で O(n² log n) になります
解答を動かした版 Python 3.14.7Node.js 24.22.1Go 1.24.2確認日 2026年11月8日
面接での 話し方の例
書き始める
「毎回いちばん重い 2 つが
欲しいので、 大きい順の ヒープに 全部入れます。 2 回取り出して、 差が 0 でなければ 差を 戻します。 玉が 1 つ以下に なったら 止め、 残りが あれば その 重さ、 無ければ 0 を 返します。 1 回で 玉が 1 つ以上減るので、 時間は O(n log n)、 記憶は O(n) です。」
書き終えたら、
「例 1 では、
ヒープは 8、 6、 3 です。 8 と 6 を 出して 差の 2 を 戻し、 3 と 2 に なります。 3 と 2 で 1 を 戻し、 1 だけ残るので 1 を 返します。」