並べ替えとツーポインタで和が 0 になる 3 つ組を全部出す

整数の​並びから、​足すと​ 0 に​なる​ 3 つの​数の​組を、​同じ組を​ 2 回出さずに​全部返します。​並べ替えて​ 1 つ目を​固定し、​残り 2 つを​ツーポインタで​探し、​重なりを​飛ばす理由まで​言える​練習です。

海外で定番の問題リスト 4 本のうち 4 本に入っています (Blind 75、NeetCode 150、Grind 75、LeetCode Top Interview 150)

問題

整数の並び nums を受け取ります。違う 3 つの位置から数を 1 つずつ選び、足すと 0 になる組を全部見つけて返す関数を書いてください。

  • 組は数の値で見ます。値が同じ 3 つの組は、選んだ位置が違っても 1 回だけ返します
  • 返す順は次のとおりに決めます。各組の中は小さい順に並べます。組どうしは、1 つ目の数が小さい組を先にし、1 つ目が同じなら 2 つ目が小さい組を先にします
  • 足して 0 になる組が無ければ、空の並び [] を返します
  • nums の長さは 3 以上 3000 以下、値はどれも -100000 以上 100000 以下です

入力と出力の例

例 1

入力

nums = [3, -2, 1, -1, 0, -3]

出力

[[-3, 0, 3], [-2, -1, 3],
 [-1, 0, 1]]

例 2

同じ値がいくつあっても、同じ組は 1 回だけ返します。-2 を 2 回使う組は無いので、-2 が 2 つあることは答えを増やしません。

入力

nums = [-2, 1, 1, 1, 0, 2, -2]

出力

[[-2, 0, 2], [-2, 1, 1]]

例 3

入力

nums = [5, -1, 7, 2]

出力

[]

面接での聞かれ方

面接では、足して目標になる 2 つを探す問題の続きとして出されることがよくあります。問題文は短く、決めるべきことは面接官からは言われません。

  • 「配列から、足して 0 になる 3 つの組を全部返してください。同じ組は重複させないでください」
  • 「同じ値が何個も入っていたら?」「組の中や組どうしの順は?」と、こちらから確かめるのを待たれています。聞かずに書き始めると、重なりの扱いで後戻りします
  • 全部の組を試す書き方を見せると、「O(n³) より速くできますか?」と続きます
  • 並べ替えとツーポインタで書くと、「重複をどう飛ばしましたか? 飛ばしすぎて答えを落としませんか?」と、飛ばし方の理由を聞かれます。ここがいちばん詰められる所です
  • 「集合 (セット) に入れて重複を消すのではだめですか?」と、別の書き方との比べを聞かれます
  • 最後に「O(n²) より速くできますか?」「4 つの組なら?」「目標が 0 でなく任意の数なら?」と、広げた形を聞かれることもあります

O(n²) より大きく速い書き方は、一般には知られていません。面接では O(n²) にたどり着き、そのうえで重なりを正しく、理由をつけて消せるかが見られています。

アルゴリズムのチートシートの「配列と文字列」に、​この​型の​要点を​表と​コードで​まとめています。

main.py
def three_sum(nums):
    # ここに書きます
    pass


# 「実行」を押すと、この行の結果が出ます
print(three_sum([3, -2, 1, -1, 0, -3]))

できているか「例で確かめる」を押すと、問題の例を全部この関数に渡して、通ったかをここに出します