JavaScript
 Computer >> コンピューター >  >> プログラミング >> JavaScript

【JavaScript】トランプのカードを連続する数字のグループに再配置できるか判定する方法

本記事では、JavaScriptを用いてトランプのカードを「指定サイズの連続した数字のグループ」に再配置できるかどうかを判定するアルゴリズムを解説します。配列のソートとハッシュマップ(オブジェクト)を組み合わせたシンプルかつ効率的なアプローチを学びましょう。

問題の概要

次のようなJavaScript関数を実装することが求められます。

  • 第1引数:数値の配列 arr
  • 第2引数:グループのサイズを表す数値 num

配列内の各数値は [1, 13] の範囲(両端を含む)に収まっており、それぞれトランプのカードに対応しています。

この関数の目的は、すべてのカードを「サイズが num であり、かつ num 枚の連続したカードで構成される」グループに分割できるかどうかを判定することです。分割が可能であれば true を、不可能であれば false を返します。

入出力の例

例えば、関数への入力が以下の場合を考えてみましょう。

入力

const arr = [1, 4, 3, 2];
const num = 2;

出力

true

出力の解説
このカードたちは、[1, 2][3, 4] の2つのグループに再配置できます。どちらのグループもサイズが2で、連続した数字で構成されているため、結果は true となります。

解決のアプローチ

この問題は、以下の手順で効率的に解くことができます。

  1. 配列を昇順にソートする — 小さい数字から順に処理することで、連続性のチェックが容易になります。
  2. 各カードの出現回数を記録する — オブジェクト(ハッシュマップ)を使い、同じ数字のカードが何枚あるかをカウントします。
  3. 先頭のカードから順にグループ化を試みる — まだ使用されていないカードを見つけたら、そこから始まる num 枚の連続したカードがすべて存在するかを確認します。存在しなければ即座に false を返せます。
  4. すべてのカードを処理できれば成功 — 全カードがグループ化できれば true を返します。

コード例

以下が実際の実装コードです。

const arr = [1, 4, 3, 2];
const num = 2;

const canRearrange = (arr = [], num = 1) => {
    // 指定した数字 n から始まる num 枚の連続カードを消費できるか確認するヘルパー関数
    const find = (map, n, num) => {
        let j = 0;
        while (j < num) {
            if (!map[n + j]) return false;
            else map[n + j] -= 1;
            j++;
        }
        return true;
    };

    let map = {};

    // 昇順にソート
    arr.sort(function(a, b) { return a - b });

    // 各カードの出現回数をカウント
    for (let n of arr) {
        map[n] = map[n] ? map[n] + 1 : 1;
    }

    // 各カードについて、未使用なら連続グループの形成を試みる
    for (let n of arr) {
        if (map[n] === 0 || find(map, n, num)) continue;
        else return false;
    }

    return true;
};

console.log(canRearrange(arr, num));

コードのポイント

  • find 関数は、ある数字 n から始まる num 枚の連続したカードがすべて残っているかを確認し、存在すればマップから消費(カウントを減算)します。
  • メインループでは、すでにカウントが0のカード(= 他のグループの一部として使用済み)はスキップし、残っているカードのみ新たなグループの起点として扱います。
  • 途中で連続カードが揃わないケースが見つかった時点で即座に false を返すため、無駄な計算を省けます。

出力結果

true

このように、配列 [1, 4, 3, 2] はサイズ2の連続グループ [1, 2][3, 4] に完全に分割できるため、関数は true を返します。

まとめ

このアルゴリズムの計算量は、ソートに O(n log n)、グループ化の処理に O(n × num) かかります。ソートによって小さい方から貪欲に(greedyに)グループを形成していくことで、正しく判定できるのがポイントです。「手札をストレート(連続した数字)ごとに整理できるか」というトランプのゲームでも応用できる考え方なので、ぜひ理解を深めておきましょう。

  1. JavaScriptで3桁の数字を並べ替えて最大の数を作る方法

    問題正の3桁の整数を1つ引数として受け取り、その各桁を並べ替えることで作れる最大の数を返すJavaScript関数を作成する必要があります。アプローチこの問題は、次の手順で解決できます。まず、入力された数値が実際に3桁であるかを検証します。100未満または999を超える場合は null を返します。数値を文字列に変換し、split() で1文字ずつの配列に分解します。sort((a, b) => b - a) を使って桁を降順に並べ替えます。これにより、大きい桁が先頭に来ます。join() で配列を文字列に結合し、単項プラス演算子(+)で数値型に戻して返します。コード例以下は上記のアプロ

  2. JavaScriptで数値をできるだけ近い値を持つn個に分割する方法

    問題 JavaScriptで関数を作成することを考えます。この関数は、第1引数として数値 num を、第2引数として数値 parts を受け取ります。 関数は、num をちょうど parts 個の数値に分割し、次の2つの条件を満たす必要があります。 各数値は互いにできるだけ近い値にすること 可能であれば偶数にすること なお、出力される数値の順序は重要ではありません。 たとえば、関数への入力が以下の場合を考えてみましょう。 入力 const num = 20; const parts = 6; 出力 const output = [3, 3, 3, 3, 4, 4]; 解き方のアプローチ