ヒープで原点にいちばん近い k 個の点を選ぶ

ヒープ問題 110

平面の点の並びから、原点 (0, 0) に近い方の k 個を選んで返します。全部を並べ替えず、近い k 個だけを「いちばん遠い物が根に来るヒープ」に持ち続ける練習です。

海外で定番の問題リスト 4 本には入っていません

問題

配達の拠点が原点 (0, 0) にあり、届け先の場所が平面の点 [x, y] の並び points で渡されます。拠点からまっすぐ測って近い方から k 件を選び、その点を並びにして返す関数を書いてください。

  • 近さは、まっすぐの距離 (x の 2 乗と y の 2 乗の和の平方根) で比べます。平方根を取らず、2 乗の和のまま比べてかまいません
  • 返す順は、近い順です。距離が同じ点どうしは、points で前にある方を先にします。選ぶときも同じ決まりで、距離が同じなら前にある点を選びます
  • points の長さは 1 以上 10000 以下、x と y はどれも -10000 以上 10000 以下の整数です
  • k は 1 以上、points の長さ以下です
  • 全部を並べ替える O(n log n) ではなく、ヒープを使って O(n log k) で選んでください

入力と出力の例

例 1

2 乗の和は順に 10、4、25、2 です。近い 2 件は、2 の [-1, 1] と 4 の [2, 0] です。

points = [[3, 1], [2, 0], [0, -5], [-1, 1]] k = 2
[[-1, 1], [2, 0]]

例 2

[0, 2] と [2, 0] と [-2, 0] は、どれも 2 乗の和が 4 で同じです。前にある 2 つを選びます。

points = [[0, 2], [2, 0], [-2, 0]] k = 2
[[0, 2], [2, 0]]

例 3

点が 1 つで、k も 1 です。

points = [[4, -3]] k = 1
[[4, -3]]

面接での聞かれ方

面接では、短く次のように渡されることが多いです。

  • 「平面の点の並びから、原点に近い k 個を返してください。並べ替えずにできますか?」
  • ヒープで書くと「なぜ近い順ではなく、遠い順のヒープなのですか?」と理由を聞かれます
  • 「平方根は要りますか?」「点が何億個も流れてくるとしたら?」と広げて聞かれます

返す順と、距離が同じ点の扱いを最初に確かめておくと、答えが 1 つに決まり、例で比べやすくなります。

ヒント

まだ開いていません

付箋は 4 枚。下の段ほど答えに近づきます

  1. ヒント 2

  2. ヒント 3

  3. ヒント 4答えに近い

アルゴリズムのチートシートの「スタックとキュー」に、この型の要点を表とコードでまとめています。

スタックは後から入れた物を先に、キューは先に入れた物を先に取り出します。括弧の対応や、順番待ちの処理の形です。

main.py
def k_closest(points: list[list[int]], k: int) -> list[list[int]]:
    """
    Goal: 何を返すか

    idea1: まず思いつく案
    -> runtime: O(?)  space: O(?)

    idea2: もっと速い案
    -> runtime: O(?)  space: O(?)

    edge case: 空、1 つだけ、など
    """
    # ここに書きます
    pass


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

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