JavaScriptで2進数の「1」同士の最長距離(バイナリギャップ)を求める方法
問題の概要
正の整数 n を受け取る JavaScript 関数を作成しましょう。この関数は、n を2進数で表したときに出現する「1」のうち、隣接する2つの1の間の最長距離(バイナリギャップ)を求めて返します。
もし隣接する1が2つも存在しない場合は、0 を返します。
「隣接」と「距離」の定義
2つの1が隣接しているとは、その間が0のみで区切られている(あるいは何も挟まれていない)状態を意味します。間に別の1が存在する場合、それらは隣接とはみなされません。
また、2つの1の距離は、それぞれのビット位置の差の絶対値として定義されます。たとえば「1001」に含まれる2つの1の距離は 3 です。
具体例
入力が 22 のとき、出力は 2 になります。その理由は以下の通りです。
- 22 の2進数表現は「10110」である
- 最初の隣接ペア(左端の1とその次の1)の距離は 2
- 2番目の隣接ペア(残りの1同士)の距離は 1
- 答えはこれらのうち大きい方、つまり 2
なお、間にもう1つの1を挟んでいるような2つの1は、隣接ペアとしてカウントできない点に注意してください。
実装例
const num = 22;
const binaryGap = (num = 1) => {
let last = -1; // 直前に見つけた「1」のビット位置(未発見なら -1)
let ans = 0; // 最大距離の答え
// 下位ビットから上位ビットへ向かって全ビットを走査
for (let i = 0; i < 32; i++) {
// 現在のビットが「1」かどうかを判定
if (((num >> i) & 1) === 1) {
// 前回の「1」がすでに見つかっていれば距離を計算し、最大値を更新
if (last >= 0) {
ans = Math.max(ans, i - last);
}
last = i;
}
}
return ans;
};
console.log(binaryGap(num));
出力結果
コンソールには次のように表示されます。
2
コードの解説
このアルゴリズムのポイントは、変数 last を使って「直前に見つけた1のビット位置」を記録しておくことです。初期値は -1 であり、「まだ1が一度も見つかっていない」状態を表します。
ループ内では (num >> i) & 1 というビット演算によって、i 番目のビットが1かどうかを判定します。1が見つかった時点で、すでに前回の1が存在していれば(last >= 0)、現在位置との差 i - last を計算し、Math.max() で最大距離を更新します。その後 last を現在の位置に更新して、走査を続けます。
時間計算量は O(log n)、空間計算量は O(1) と非常に効率的で、余分な配列や文字列変換も不要です。ビット演算を活用することで、2進数への変換を行わずとも簡潔に問題を解けるのが魅力です。
-
JavaScriptで学ぶ二分探索木(Binary Search Tree)の基本と操作方法
二分探索木とは二分探索木は、通常の木構造とは異なる特別な性質を持つデータ構造です。この性質により、データの検索・挿入・削除を効率的に行うことができます。二分探索木では、各ノードが次のルールに従わなければなりません。ノードの左の子は、必ず親ノードより小さい値を持つノードの右の子は、必ず親ノードより大きい値を持つこの規則が成り立つことで、値を探す際に「目的の値より小さければ左へ、大きければ右へ」と分岐をたどるだけで済み、探索範囲を毎回半分に絞り込めます。そのため、整列された配列に対する二分探索と同様の効率性が得られます。本記事を含む木構造のセクションでは、主にこの二分探索木を中心に解説を進めていき
-
JavaScriptの二分木(バイナリツリー)とは?基本概念と重要用語を徹底解説
二分木(バイナリツリー)は、データの格納を目的として使用される特殊なデータ構造です。最大の特徴は、各ノードが持てる子ノードの数が2つまでという条件にあります。二分木は、整列済み配列と連結リストの両方の長所を兼ね備えた構造です。検索はソートされた配列と同等の速さで行え、データの挿入や削除も連結リストと同様に高速に実行できます。そのため、大量のデータを効率的に扱いたい場合に非常に有用なデータ構造といえます。以下は、二分木の構造を示したイラストです。図には、このあと解説する重要な用語も含まれています。二分木における重要な用語二分木を理解するうえで押さえておきたい、主要な用語を以下にまとめました。パス