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

C++で行列を対角線方向にソートする方法

N×M の行列が与えられたとき、左上から右下へ向かう各対角線に沿って要素を昇順に並べ替えることを考えます。例えば、次のような行列があったとしましょう。

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 ずつ増やします。

  • メインの処理では、以下を行います。

  • n に行数、m に列数を取得します。

  • i を 0 から n−1 まで繰り返します。

    • solve(i, 0, mat) を呼び出します(各行の先頭から始まる対角線を処理)。

  • j を 1 から m−1 まで繰り返します。

    • solve(0, j, mat) を呼び出します(最初の行の各列から始まる対角線を処理)。

  • 結果の mat を返します。

この方法では、最長の対角線の長さは min(n, m) であり、対角線の本数は n + m − 1 本となるため、計算量は O((n + m)・min(n, m)・log(min(n, m))) 程度になります。

C++での実装例

理解を深めるために、以下の実装を見てみましょう。

#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]]
  1. C++で解く「Maze III」:ボールを最短距離で穴に落とすアルゴリズム

    問題の概要 空きスペースと壁からなる迷路の中に、ボールが1つ置かれています。ボールは空きスペース上を上(u)・下(d)・左(l)・右(r)のいずれかの方向に転がって移動できますが、壁にぶつかるまで停止しません。ボールが停止した時点で、次の方向を選択できます。また、迷路内には穴(hole)が1つあり、ボールが穴の位置まで転がると、その穴に落ちます。 ボールの初期位置・穴の位置・迷路の情報が与えられたとき、ボールを最短距離で穴に落とすための移動手順を求めます。ここでいう距離とは、スタート地点(含まない)から穴(含む)までにボールが通過した空きスペースの数として定義されます。 移動方向は「u」「d

  2. C++で解くスパイラル行列 III:時計回りに全マスを訪問するアルゴリズム

    本記事では、R行C列の2次元グリッドを時計回りの渦巻き(スパイラル)状に巡回し、すべてのマスを訪問した順に座標を求める問題「スパイラル行列 III」をC++で解く方法を解説します。 問題の概要 R行C列の2次元グリッドを考えます。スタート地点は (r0, c0) で、最初は東向きに面しています。グリッドの北西の角は第1行・第1列に位置し、南東の角は最終行・最終列にあります。 私たちは時計回りの渦巻き状に歩きながら、グリッド内のすべてのマスを訪問します。途中でグリッドの境界外に出た場合でも、そのまま外側を歩き続け、後で再びグリッド内に戻ることがあります。 求めるのは、訪問した順番に並べたグリッド