ある範囲に含まれている整数の個数《二分探索》

二分探索メニュー【値の探索・lower_bound・upper_bound・ある範囲に含まれている整数の個数】

【値の探索】コード 1/2

reader.on('close', () => {
   const binary_search = (A, n, k) => {  // A = ソート済の数列, n = 数列のサイズ, k = 探索する値
      let [left, right] = [0, n];  // left = 探索範囲の左端, right = 探索範囲の右端
      while (left <= right) {
         const mid = Math.floor((left + right) / 2);  // mid = 探索範囲(left ~ right)の中央
         if (A[mid] === k) {  // midk なら…
            return true;  // true を返す
         } else if (A[mid] > k) {  // 探索範囲の中央が k より大きければ…
            right = mid - 1;  // right (範囲の右端)を mid - 1 へ移動 (検索範囲をA[mid]より小さい値に限定)
         } else {  // 探索範囲の中央が k より小きければ
            left = mid + 1;  // left (範囲の左端)を mid + 1 へ移動 (検索範囲をA[mid]より大きい値に限定)
         }
      }
      return false;
   };
   const [n, q] = [lines[0], lines[2]].map(Number);
   const A = lines[1].split(' ').map(Number);
   for (let i = 3; i < q + 3; i++) {
      const k = Number(lines[i]);
      if (A[0] === k || A[n - 1] === k) {
         console.log('Yes');
      } else {
         console.log(binary_search(A, n - 1, k) ? 'Yes' : 'No');
      }
   }
});
hogeちゃんの画像

問題文の模擬コード通りだと こんな感じになりました。

【値の探索】コード 2/2

reader.on('close', () => {
   const binary_search = (left, right, k) => {  // left = 探索範囲の左端, right = 探索範囲の右端, k = 探索する値
      if (left > right) {  // left(探索範囲の左端)が right (探索範囲の右端)より大きくなれば…
         return false;  // k は含まれていなかったことになるので false を返す
      }
      const mid = Math.floor((left + right) / 2);  // 探索範囲(left ~ right)の中央
      if (A[mid] === k) {  // A[mid]が k なら…
         return true;  // true を返す
      } else if (A[mid] > k) {  // 範囲の中央の値が k より大きければ…
          return binary_search(left, mid - 1, k);  // rightmid - 1 にして再帰呼出し
      } else if (A[mid] < k) {  // 範囲の中央が k より小さければ
          return binary_search(mid + 1, right, k);  // leftmid + 1 にして再帰呼出し
      }
   };
   const [n, q] = [lines[0], lines[2]].map(Number);
   const A = lines[1].split(' ').map(Number);
   for (let i = 3; i < q + 3; i++) {
      if (A[0] === k || A[n - 1] === k) {
         console.log('Yes');
      } else {
         console.log(binary_search(0, n - 1, k) ? 'Yes' : 'No');
      }
   }
});
hogeちゃんの画像

再起関数にするとこんな感じ?

【lower_bound】コード 1/3

reader.on('close', () => {
   const binary_search = (a, n, k) => {  // a = 配列(テストの点数・降順), n = 要素数 - 1, k = 検索する値
      let [left, right] = [0, n];  // left = 合格判定済のindex, right = 探索範囲の右端
      while (left < right) {
         const mid = Math.ceil((left + right) / 2);  // (left + right) / 2 を 切り上げ const mid = Math.floor((left + right) / 2); // (left + right) / 2 を 切り上げ
         // const mid = Math.floor((left + right) / 2); // (left + right) / 2 を 切り下げ
         if (a[mid] < k) {  // a[mid]が k 未満ならa[mid]〜は不合格なので…
            right = mid - 1;  // right (範囲の右端)を mid - 1 へ移動 (検索範囲をA[mid]より大きい値に限定)
            // right = mid; 
         } else {  // a[mid]が k 以上なら a[mid]まで合格が確定するので…
            left = mid;  // left (合格判定済のindex)は mid
            // left = mid + 1;
         }
      }
      return left;  // a[left]まで合格が確定
    };
   const [N, q] = [lines[0], lines[2]].map(Number);
   const A = lines[1].split(' ').map(Number).sort((s, b) => b - s);  // 降順ソート
   for (let i = 3; i < q + 3; i++) {
      const K = Number(lines[i]);  // K 点以上で合格
      if (A[0] < K) {  // A の先頭の値が K 未満なら…
         console.log(0);  // 合格者 なし
      } else if (A[N - 1] >= K) {  // A の後尾の値が K 点以上なら…
         console.log(N);  // 全員合格
      } else {  // それ以外
         console.log(binary_search(A, N - 1, K) + 1);  // 合格者の人数
         // console.log(binary_search(A, N - 1, K));
      }
   }
});
hogeちゃんの画像

k_i 点以上の人数を求めたいので 配列 A を 大きい順に並べて二分探索しました。k_i 点の人も含むので mid の値は (left + right) / 2 を 切り上げて求めた上で left の値を return しています。

【lower_bound】コード 2/3

reader.on('close', () => {
   const binary_search = (a, n, k) => {  // a = 数列(昇順), n = a の要素数 - 1, k = 合格点
      let [left, right] = [0, n];  // left = 不合格ライン, right = 不合格ライン
      while (left < right) {  // leftright) より小さい間…
         const mid = Math.floor((left + right) / 2);  // mid = 探索範囲(left ~ right)の中央
         if (a[mid] < k) {  // a[mid]が k 未満なら a[0] ~ a[mid]は不合格なので…
            left = mid + 1;  // a[0] ~ a[mid]は除外
         } else if (a[mid] >= k) {  // a[mid]が k 以上なら…
            right = mid;  // a[0] ~ a[mid] ~ 合格
         }
      }
      return right;  // a[right] ~ 合格
   };
   const [N, q] = [lines[0], lines[2]].map(Number);
   const A = lines[1].split(' ').map(Number).sort((s, b) => s - b);
   for (let i = 3; i < q + 3; i++) {
      const k = Number(lines[i]);
      if (A[N - 1] < k) {
         console.log(0);
      } else if (A[0] >= k) {
         console.log(N);
      } else {
         const border = binary_search(A, N, k);  // 合格者の最小index(不合格者数) 
         console.log(N - border);  // N - 不合格者数 = 合格者数
      }
   }
});
hogeちゃんの画像

問題文の模擬コードを参照しています。

【lower_bound】コード3/3

reader.on('close', () => {
   const binary_search = (a, left, right, k) => {
      if (left >= right) {
         return right;
      }
      const mid = Math.floor((left + right) / 2);
      if (a[mid] < k) { 
         return binary_search(a, mid + 1, right, k);
      } else {
         return binary_search(a, left, mid, k);
      }
   };
   const [N, q] = [lines[0], lines[2]].map(Number);
   const A = lines[1].split(' ').map(Number).sort((s, b) => s - b);
   for (let i = 3; i < q + 3; i++) {
      const k = Number(lines[i]);
      const border = binary_search(A, 0, N, k);
      console.log(N - border);
   }
});
hogeちゃんの画像

コード 2/3を再起関数にするとこんな感じになりました。

【upper_bound】コード

reader.on('close', () => {
   const binary_search = (A, n, k) => {
      let [left, right] = [0, n];
      while (left < right) {
         let mid = Math.floor((left + right) / 2);
         if (A[mid] <= k) {
            left = mid + 1;
         } else {
            right = mid;
         }
      }
      return right;
   };
   const [n, q] = [lines[0], lines[2]].map(Number);
   const A = lines[1].split(' ').map(Number).sort((s, b) => s - b);
   for (let i = 3; i < q + 3; i++) {
      const k = Number(lines[i]);
      if (A[0] <= K) {  // A の先頭の値が K 以下なら…
         console.log(0);  // 合格者 なし
      } else if (A[N - 1] > K) {  // A の後尾の値が K より高得点なら…
         console.log(N);  // 全員合格
      } else {  // それ以外
         const border = binary_search(A, n, k);
         console.log(n - border);
      }
   }
});
hogeちゃんの画像

【STEP: 3】の コード中の不等号記号が変えただけ…。

【ある範囲に含まれている整数の個数】コード

reader.on('close', () => {
   const binary_search_lo = (A, n, k) => {
      let [left, right] = [0, n];
      while (left < right) {
         let mid = Math.ceil((left + right) / 2);
         if (A[mid] < k) {
            right = mid - 1;
         } else {
            left = mid;
         }
      }
      return left;
   };

   const binary_search_up = (A, n, k) => {
      let [left, right] = [0, n];
      while (left < right) {
         const mid = Math.ceil((left + right) / 2);
         if (A[mid] <= k) {
            right = mid - 1;
         } else {
            left = mid;
         }
      }
      return left;
   };
   const [n, q] = [lines[0], lines[2]].map(Number);
   const A = [0, ...lines[1].split(' ').map(Number).sort((s, b) => b - s)];
   for (let i = 3; i < q + 3; i++) {
      const [l, r] = lines[i].split(' ').map(Number);
      const over = binary_search_lo(A, n, l);
      const less = n - binary_search_up(A, n, r);
      console.log(over + less - n);
      // console.log(over, less);
   }
});
hogeちゃんの画像

とりあえずup。

コメント