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

C++で行列の対角要素を昇順にソートするプログラムの実装方法

n × m の行列 Mat が与えられたとき、左上から右下へ向かう各対角線(斜め方向)に沿って要素を昇順に並べ替えることを考えます。つまり、すべての対角線上の要素がそれぞれ独立にソートされた状態にします。

例えば、次のような入力行列があったとします。

3311
2212
1112

この場合、出力される行列は次のようになります。

1111
1222
1233

解決のためのアプローチ

この問題は「同じ対角線上にある要素を一度取り出してソートし、元の位置に書き戻す」というシンプルな発想で解決できます。具体的には、以下の手順に従います。

  • solve() というメソッドを定義します。引数として対角線の開始位置 si、sj と行列 mat を受け取ります
  • n := 行数、m := 列数 とします
  • 一時的な配列 temp を用意します
  • i := si、j := sj、index := 0 で初期化します
  • i < n かつ j < m の間、次の処理を繰り返します
    • mat[i][j] の値を temp に追加し、i と j をそれぞれ 1 増やします
  • temp 配列をソートします
  • index := 0、i := si、j := sj に戻します
  • i < n かつ j < m の間、次の処理を繰り返します
    • mat[i][j] := temp[index] とし、i、j、index をそれぞれ 1 増やします
  • main メソッドでは、次の処理を行います
  • n := 行数、m := 列数 とします
  • i を 0 から n − 1 まで繰り返します
    • solve(i, 0, mat) を呼び出します(最初の列を起点とする対角線)
  • j を 1 から m − 1 まで繰り返します
    • solve(0, j, mat) を呼び出します(最初の行を起点とする対角線)
  • mat を返します

ポイントは、対角線の走査を「最初の列の各行」と「最初の行の各列」の 2 回のループで網羅できることです。これにより、行列内のすべての対角線を漏れなく処理できます。

計算量について

各対角線の長さは最大でも min(n, m) であるため、全体の時間計算量は O(n × m × log(min(n, m))) 程度になります。空間計算量は一時配列 temp の分だけ余分に必要ですが、対角線ごとに使い回すため O(min(n, m)) で抑えられます。

実装例

それでは、理解を深めるために実際のコードを見てみましょう。

#include <bits/stdc++.h>
using namespace std;
void print_vector(vector<vector<auto> > v){
    cout << "[";
    for(int i = 0; i<v.size(); i++){
        cout << "[";
        for(int j = 0; j <v[i].size(); j++){
            cout << v[i][j] << ", ";
        }
        cout << "],";
    }
    cout << "]"<<endl;
}
class Solution {
    public:
    void solve(int si, int sj, vector < vector <int> > &mat){
        int n = mat.size();
        int m = mat[0].size();
        vector <int> temp;
        int i = si;
        int j = sj;
        int idx = 0;
        while(i < n && j < m){
            temp.push_back(mat[i][j]);
            i++;
            j++;
        }
        sort(temp.begin(), temp.end());
        idx = 0;
        i = si;
        j = sj;
        while(i < n && j < m){
            mat[i][j] = temp[idx];
            i++;
            j++;
            idx++;
        }
    }
    vector<vector<int>> diagonalSort(vector<vector<int>>& mat) {
        int n = mat.size();
        int m = mat[0].size();
        for(int i = 0; i <n; i++){
            solve(i, 0, mat);
        }
        for(int j = 1; j < m; j++){
            solve(0, j, mat);
        }
        return mat;
    }
};
main(){
    vector<vector<int>> v = {{3,3,1,1},{2,2,1,2},{1,1,1,2}};
    Solution ob;
    print_vector(ob.diagonalSort(v));
}

入力

{{3,3,1,1},{2,2,1,2},{1,1,1,2}}

出力

[[1, 1, 1, 1], [1, 2, 2, 2], [1, 2, 3, 3]]

このように、solve() メソッドで対角線上の要素を収集・ソート・書き戻しし、diagonalSort() メソッドですべての対角線に対して solve() を適用することで、行列全体を対角方向に昇順ソートすることができました。

  1. C++で与えられた行列を対角行列に変換するプログラムの作成方法

    n×n のサイズで与えられた行列を、種類を問わず対角行列へ変換するのが本記事のテーマです。C++による実装方法を、考え方・アルゴリズム・サンプルコード・実行結果まで含めてわかりやすく解説します。 対角行列とは? 対角行列とは、n×n の正方行列のうち、対角成分(行番号と列番号が一致する要素)以外のすべての要素が 0 である行列を指します。対角成分自体には任意の値を入れることができます。 下図は、非対角成分を 0 に変換するイメージです。 | 1 2 3 | | 1 0 3 | | 4 5 6 | → | 0 5 0 | | 7 8 9 |

  2. C++で対角行列・スカラー行列を判定するプログラムの書き方

    行列 M[r][c] が与えられたとき、「r」は行数、「c」は列数を表し、r = c のとき正方行列となります。本記事では、与えられた正方行列が対角行列であるか、スカラー行列であるかを判定し、該当する場合には「yes」を出力する方法を解説します。 対角行列とは 正方行列 m[][] が対角行列であるのは、主対角線以外の要素がすべてゼロである場合、かつその場合に限ります。 下図のように、赤色で示された要素が主対角成分(非ゼロ)であり、それ以外の要素はすべてゼロになっているため、この行列は対角行列です。 入出力例 Input: m[3][3] = { {7, 0, 0}, {0, 8, 0}