問題
小さいnums を
- 二分探索木は、
どの 節でも、 左の 部分木の 値は 全部 その 節より 小さく、 右の 部分木の 値は 全部 その 節より 大きい木です - 作り方は 1 通りに
決めます。 いま使う 範囲の 左端の 番号と 右端の 番号を 足して 2 で 割り、 端数を 切り捨てた番号の 値を 根にします。 その 左側の 範囲から 左の 部分木を、 右側の 範囲から 右の 部分木を、 同じ 決め方で作ります - 新しい
節は、 Python では TreeNode(値)、JavaScript では new TreeNode(値)、Go では &TreeNode{}で作れます。 どれも val(値)、left(左の子)、 right(右の子) を 持ち、 子は 最初は 空です。 Go では Val、Left、Rightと大文字で 始まります - 数の
個数は 1 以上 500 以下、 値は どれも -1000 以上 1000 以下で、 同じ値は ありません
出力のnull で、null は
入力と出力の例
例 1
7 つの
入力
nums = [2, 5, 8, 11, 14, 17, 20]出力
[11, 5, 17, 2, 8, 14, 20]例 2
5 つの
入力
nums = [-10, -3, 0, 5, 9]出力
[0, -10, 5, null, -3, null, 9]例 3
2 つだけの
入力
nums = [3, 6]出力
[3, null, 6]面接での聞かれ方
面接では、
- 「小さい
順の 並びから、 高さの 釣り合った 二分探索木を 作ってください」 - 書き終えた
あとに 「なぜまん中を 根に すると 高さが そろうのですか?」と 理由を 聞かれます - 「並びではなく、
小さい 順の 連結リストで 渡されたら どうしますか?」と 形を 変えて 聞かれる こともあります
まん中が 2 つある
アルゴリズムのチートシートの「再帰と木」に、この型の要点を表とコードでまとめています。
まず 考える手順
段 1
まず、
段 2
次に、
段 3
左右の
段 4
範囲を
解答
Python
Python
def sorted_to_bst(nums):
last = len(nums) - 1
return grow(nums, 0, last)
def grow(nums, lo, hi):
if lo > hi:
return None
mid = (lo + hi) // 2
node = TreeNode(nums[mid])
node.left = grow(
nums, lo, mid - 1
)
node.right = grow(
nums, mid + 1, hi
)
return nodeJavaScript
JavaScript
function sortedToBst(nums) {
const last = nums.length - 1;
return grow(nums, 0, last);
}
function grow(nums, lo, hi) {
if (lo > hi) {
return null;
}
const mid = Math.floor(
(lo + hi) / 2
);
const node = new TreeNode(
nums[mid]
);
node.left = grow(
nums, lo, mid - 1
);
node.right = grow(
nums, mid + 1, hi
);
return node;
}Go
Go
func sortedToBst(
nums []int,
) *TreeNode {
last := len(nums) - 1
return grow(nums, 0, last)
}
func grow(
nums []int, lo, hi int,
) *TreeNode {
if lo > hi {
return nil
}
mid := (lo + hi) / 2
v := nums[mid]
node := &TreeNode{Val: v}
node.Left = grow(
nums, lo, mid-1,
)
node.Right = grow(
nums, mid+1, hi,
)
return node
}番号は 0 以上Math.floor は、// と
計算量
- 時間は O(n) です。
どの 数からも 節を 1 つ作るだけで、 範囲を 切り出す写しは 作りません - 使う
記憶は、 返す木の n 個の 節の ほかに O(log n) です。 まん中で 半分ずつに 分けるので、 再帰の 深さは 木の 高さ、 つまり log n 程度で 止まります
よく ある間違い
- 範囲の
切り出しで 写しを作る。 範囲を 切り出した 新しい 並びを 毎回渡すと、 写しの 分だけ 時間と 記憶が 増え、 全体で O(n log n) に なります。 番号だけを渡します - 右端の
扱いが混ざる。 右端を 「含む」 番号に するか 「含まない」 番号に するかを 途中で 取り違えると、 数が 1 つ抜けたり、 同じ数を 2 回使ったりします。 ここでは 両端を 含む形に そろえ、 空の 判定は lo > hiにします - まん中の
決め方を 決めずに書く。 例 3 のように 数が 偶数個の 範囲では、 まん中の 候補が 2 つあります。 左寄りか 右寄りかで 答えの 木が 変わるので、 問題の 決め方 (切り捨てで 左寄り) に 合わせます
解答を動かした版 Python 3.12.14Node.js 22.23.4Go 1.24.6確認日 2026年10月30日
面接での 話し方の例
書き始める
「並びが
小さい 順なので、 どれかを 根に すると 左側が そのまま 左の 部分木、 右側が 右の 部分木に なります。 まん中を 根に すれば 左右の 数の 差が 1 以下に なり、 高さが そろいます。 範囲の 両端の 番号を 渡す再帰で、 まん中は 切り捨てで 左寄りにします。 写しを 作らないので 時間は O(n)、 再帰の 深さは log n 程度です。」
書き終えたら、
「例 2 で
たどります。 番号 0 から 4 の まん中は 2 で、 0 が 根です。 左の 範囲は 番号 0 と 1 で、 まん中は 切り捨てで 0 なので -10、 その右に -3 が 付きます。 右の 範囲は 番号 3 と 4 で、 5 が 根、 その右に 9 が 付きます。」
