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

JavaScriptで2次元配列の行・列の最大値を効率よく数える方法

問題の概要

本記事では、整数を格納した2次元配列(マトリクス)を引数として受け取り、「自分が属する行と列のどちらにおいても最大値である」要素の個数を返すJavaScript関数を実装します。

一見シンプルな条件ですが、全要素を毎回素朴に比較すると非効率になりがちです。そこで、行ごとの最大値と列ごとの最大値を先に計算しておくことで、O(rows × cols) の計算量でスマートに解くことができます。

入力例と期待される出力

たとえば、次のような配列が入力された場合を考えてみましょう。

const arr = [
    [21, 23, 22],
    [26, 26, 25],
    [21, 25, 27]
];

このとき、関数が返すべき値は 3 です。

const output = 3;

条件を満たしているのは 26、26、27 の3つの要素です。具体的には、以下の位置にある要素が該当します。

  • 2行目・1列目の 26(行の最大値=26、列の最大値=26)
  • 2行目・2列目の 26(行の最大値=26、列の最大値=26)
  • 3行目・3列目の 27(行の最大値=27、列の最大値=27)

実装コード

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

const arr = [
    [21, 23, 22],
    [26, 26, 25],
    [21, 25, 27]
];

const countGreatest = (matrix = []) => {
    let rows = matrix.length;
    if (rows == 0) {
        return 0;
    }
    let cols = matrix[0].length;
    const colMax = [];
    const rowMax = [];
    let res = 0;

    // ステップ1:各行・各列の最大値を求める
    for (let r = 0; r < rows; ++r) {
        for (let c = 0; c < cols; ++c) {
            rowMax[r] = Math.max(rowMax[r] || 0, matrix[r][c]);
            colMax[c] = Math.max(colMax[c] || 0, matrix[r][c]);
        }
    }

    // ステップ2:行と列の両方で最大となる要素をカウント
    for (let r = 0; r < rows; ++r) {
        for (let c = 0; c < cols; ++c) {
            if (matrix[r][c] == rowMax[r] && matrix[r][c] == colMax[c]) {
                res++;
            }
        }
    }
    return res;
};

console.log(countGreatest(arr));

実行結果

コンソールには次のように出力されます。

3

コードのポイント解説

  1. 早期リターン: 配列が空(rows == 0)の場合は、比較対象が存在しないため 0 を即座に返します。
  2. 最大値の前計算: 最初の二重ループで、rowMax[r](r行目の最大値)と colMax[c](c列目の最大値)をまとめて更新します。Math.max() を活用することでコードが非常に簡潔になります。
  3. 条件判定: 2回目の走査では、要素が「自分の属する行の最大値」かつ「自分の属する列の最大値」と一致する場合のみカウントを増やします。

補足:負の数を扱う場合の注意点

上記コードでは最大値の初期値として 0rowMax[r] || 0 の部分)を採用しています。そのため、配列に負の整数が含まれる場合は誤った結果になる可能性があります。汎用的に使いたい場合は、初期値を -Infinity に置き換えると安全です。

rowMax[r] = Math.max(rowMax[r] ?? -Infinity, matrix[r][c]);
colMax[c] = Math.max(colMax[c] ?? -Infinity, matrix[r][c]);

計算量

時間計算量:O(rows × cols)
空間計算量:O(rows + cols)(rowMax と colMax の保存分)

行列全体を2回なぞるだけで済むため、大きなサイズの配列でも高速に処理できます。「行・列ごとの最大値を先に求めておく」という発想は、似たようなマトリクス系の問題にも応用できるテクニックなので、ぜひ覚えておきましょう。

  1. JavaScriptで配列の最初の要素と最後の要素を取得する方法

    配列とは、複数の要素をひとまとめにして管理するデータ構造です。各要素にはそれぞれ固有のインデックス番号が割り当てられており、このインデックスを使うことで任意の要素へアクセスできます。ただし、最後の要素に関しては、配列に含まれる要素数が分からなければインデックスも確定しないため、少し工夫が必要です。本記事では、これらの方法をわかりやすく解説します。 最初の要素へのアクセス JavaScriptでは配列のインデックスは「0」から始まるため、最初の要素の位置は常に分かっています。そのため、値の取得は非常に簡単です。配列を arr とすると、最初の要素の値は arr[0] で表されます。 サンプ

  2. JavaScriptで数値を丸める・切り捨てる方法|Math.round()とMath.trunc()の違いと使い方

    JavaScriptには、小数を整数へ変換するためのメソッドとして、Math.round() と Math.trunc() の2つが標準で用意されています。Math.round():小数点以下を四捨五入し、最も近い整数に丸めます。例えば「3.5」は「4」に、「3.4」は「3」になります。Math.trunc():小数点以下を単純に取り除き、整数部分だけを残します。「3.9」も「3.1」もどちらも「3」になります。それぞれの特徴を踏まえながら、実際のコード例を見ていきましょう。Math.round() と Math.trunc() の違いメソッド動作例(3.999)Math.round()四捨五