問題
整数のnums と、target をnums からtarget に
- 値の
組み合わせが 同じ ものは 1 回だけ返します。 たとえば [1, 1, 2]から選んだ 2 つの 1は、どちらの 1を使っても 同じ組です - 組の
中は 小さい 順に 並べます。 組どうしは 辞書の 順 (先頭の 数から 比べ、 小さい ほうが 先。 同じなら次の 数で 比べる ) に並べます - そういう
組が 1 つも 無い とき、 numsの長さが 4 より 短い ときは、 空の 並び []を返します numsの長さは 1 以上 200 以下、 値は どれも 絶対値が 10 億以下、 targetは絶対値が 10 億以下です。 4 つの 和は 40 億近くに なることがあります
入力と出力の例
例 1
入力
nums = [3, -1, 0, 2, -2, 1]
target = 2出力
[[-2, -1, 2, 3],
[-2, 0, 1, 3],
[-1, 0, 1, 2]]例 2
同じ-1 と 0 と 0 と 1 の-1 と1 を
入力
nums = [-1, 0, 1, -1, 0, 1]
target = 0出力
[[-1, -1, 1, 1],
[-1, 0, 0, 1]]例 3
5 つ
入力
nums = [2, 2, 2, 2, 2]
target = 8出力
[[2, 2, 2, 2]]面接での聞かれ方
面接では、
- 「では 4 つなら
どうしますか? 3 つの ときの 書き方を 使えますか?」と、 前の 解き方を 広げられるかを 聞かれます - 「同じ組を 2 回出さないように
するには、 どこで 飛ばしますか?」と、 重なりの 消し方を 細かく 聞かれます - 書き終えた
あとに 「5 つ、 6 つと 増えたら どう 書きますか?」 「4 つの 和は、 どの 型の 整数に 入りますか?」と続きます
外側で 1 つずつ数を
アルゴリズムのチートシートの「配列と文字列」に、この型の要点を表とコードでまとめています。
まず 考える手順
段 1
まずは
段 2
並びをtarget から
段 3
重なりは、-1 を 1 回試したら、-1 から
段 4
この
段 5
和の
解答
Python
Python
def four_sum(nums, target):
a = sorted(nums)
return k_sum(
a, 0, 4, target
)
def k_sum(a, lo, k, goal):
if k == 2:
return two_sum(
a, lo, goal
)
out = []
for i in range(lo, len(a)):
if dup(a, i, lo):
continue
x = a[i]
subs = k_sum(
a, i + 1, k - 1,
goal - x,
)
for t in subs:
out.append([x] + t)
return out
def two_sum(a, lo, goal):
start = lo
hi = len(a) - 1
out = []
while lo < hi:
if dup(a, lo, start):
lo += 1
continue
x = a[lo]
y = a[hi]
if x + y < goal:
lo += 1
elif x + y > goal:
hi -= 1
else:
out.append([x, y])
lo += 1
hi -= 1
return out
def dup(a, i, first):
if i == first:
return False
return a[i] == a[i - 1]JavaScript
JavaScript
function fourSum(nums, target) {
const a = [...nums].sort(
(p, q) => p - q
);
return kSum(a, 0, 4, target);
}
function kSum(
a, lo, k, goal
) {
if (k === 2) {
return twoSum(a, lo, goal);
}
const out = [];
const n = a.length;
for (let i = lo; i < n; i++) {
if (dup(a, i, lo)) {
continue;
}
const x = a[i];
const rest = goal - x;
const subs = kSum(
a, i + 1, k - 1, rest
);
for (const t of subs) {
out.push([x, ...t]);
}
}
return out;
}
function twoSum(a, lo, goal) {
const start = lo;
let hi = a.length - 1;
const out = [];
while (lo < hi) {
if (dup(a, lo, start)) {
lo++;
continue;
}
const x = a[lo];
const y = a[hi];
if (x + y < goal) {
lo++;
} else if (x + y > goal) {
hi--;
} else {
out.push([x, y]);
lo++;
hi--;
}
}
return out;
}
function dup(a, i, first) {
if (i === first) {
return false;
}
return a[i] === a[i - 1];
}Go
Go
import "sort"
func fourSum(
nums []int, target int,
) [][]int {
a := append([]int{}, nums...)
sort.Ints(a)
return kSum(a, 0, 4, target)
}
func kSum(
a []int, lo, k, goal int,
) [][]int {
if k == 2 {
return twoSum(a, lo, goal)
}
out := [][]int{}
for i := lo; i < len(a); i++ {
if dup(a, i, lo) {
continue
}
x := a[i]
subs := kSum(
a, i+1, k-1, goal-x,
)
for _, t := range subs {
q := []int{x}
q = append(q, t...)
out = append(out, q)
}
}
return out
}
func twoSum(
a []int, lo, goal int,
) [][]int {
start := lo
hi := len(a) - 1
out := [][]int{}
for lo < hi {
if dup(a, lo, start) {
lo++
continue
}
x, y := a[lo], a[hi]
if x+y < goal {
lo++
} else if x+y > goal {
hi--
} else {
p := []int{x, y}
out = append(out, p)
lo++
hi--
}
}
return out
}
func dup(
a []int, i, first int,
) bool {
if i == first {
return false
}
return a[i] == a[i-1]
}k_sum (kSum) は、k が 2 にint は 64 ビットの
計算量
- 時間は O(n³) です。
外の 2 段で 数を 2 つ 決める 組が O(n²) 通り あり、 そのたびに 両端の 指が O(n) 進みます。 並べ替えの O(n log n) は これより 小さく 収まります - 使う
記憶は、 返す組を 除くと O(n) です。 並べ替えた 写しの 分で、 呼び出しの 深さは 3 段で 決まっています - 素直な 4 重の
繰り返しの O(n⁴) から、 並べ替えと 両端の 指で 一段縮めました。 同じ形で k 個の 和に すると O(nᵏ⁻¹) です
よく ある間違い
- 重なりを
飛ばす条件を、 段の 最初の 数にも かけてしまう。 i > loを見ずに 「左と同じなら飛ばす」と 書くと、 1 つ上の 段で 決めた 数と 同じ 値を、 この 段で 使えなくなります。 例 3 の [2, 2, 2, 2]が見つから なくなります - 和を 32 ビットの
整数で 計算する。 Java の intや C のintでa + b + c + dを計算すると、 10 億が 4 つで 40 億に なり、 あふれて 負の 数に なります。 和や 「目標から引いた残り」は 64 ビットで持ちます - JavaScript で
比べる 関数を 渡さずに sortする。 何も渡さないと 文字として 並べるので、 [10, -3, 2]が[-3, 10, 2]になります。 小さい 順が 崩れると、 両端の 指の 寄せ方も 重なりの 飛ばし方も 成り 立ちません。 (p, q) => p - qを渡します
解答を動かした版 Python 3.12.14Node.js 22.23.4Go 1.24.6確認日 2026年10月30日
面接での 話し方の例
書き始める
「4 重の
繰り返しだと O(n⁴) です。 並べ替えてから、 左から 数を 1 つずつ 決めて、 残りを 1 つ少ない 和の 問題に 渡します。 最後の 2 つは 両端の 指で O(n) で 探すので、 全体は O(n³) です。 同じ段ですぐ左と 同じ値は 飛ばすので、 重なる 組は 出ません。 和は 40 億近くに なるので 64 ビットで 持ちます。」
書き終えたら、
「例 1 は
並べ替えると [-2, -1, 0, 1, 2, 3]です。-2と-1を決めると、 残りの 2 つで 5 を 作るので、 2と3が見つかります。 -2と0なら 4 で、1と3です。-1と0なら 3 で、1と2です。小さい 数から 決めているので、 この 3 つが そのまま 辞書の 順に 並びます。」
