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

【C++】配列をソートするために必要な最小スワップ回数を求める方法

問題文

N個の互いに異なる要素からなる配列が与えられたとき、その配列を昇順にソートするために必要な最小のスワップ(要素の入れ替え)回数を求めます。

たとえば、配列が {4, 2, 1, 3} の場合、必要なスワップは 2回 です。

  • arr[0](4)と arr[2](1)を交換 → 配列は {1, 2, 4, 3} になります
  • arr[2](4)と arr[3](3)を交換 → 配列は {1, 2, 3, 4} となり、ソート完了です

アルゴリズム

  1. C++で pairvector を作成します。first には配列の値を、second には元の配列におけるインデックスを格納します。
  2. pair の first(値)を基準にして vector をソートします。
  3. vector を先頭から走査し、各値に紐づくインデックスが正しい位置かどうかを確認します。正しくない場合は、その要素が正しい位置に収まるまでスワップを繰り返し、同時にスワップ回数をカウントしていきます。

サンプルコード

#include <bits/stdc++.h>
using namespace std;
int getMinSwaps(int *arr, int n) {
   vector<pair<int, int>> vec(n);
   for (int i = 0; i < n; ++i) {
      vec[i].first = arr[i];
      vec[i].second = i;
   }
   sort(vec.begin(), vec.end());
   int cnt = 0;
   for (int i = 0; i < n; ++i) {
      if (vec[i].second == i) {
         continue;
      }
      swap(vec[i].first,vec[vec[i].second].first);
      swap(vec[i].second,vec[vec[i].second].second);
      if (i != vec[i].second) {
         --i;
      }
         ++cnt;
   }
   return cnt;
}
int main() {
   int arr[] = {4, 2, 1, 3};
   int n = sizeof(arr) / sizeof(arr[0]);
   cout << "Minimum swaps = " << getMinSwaps(arr, n) <<
   endl;
   return 0;
}

上記のプログラムをコンパイルして実行すると、次の出力が得られます。

出力

Minimum swaps = 2

ポイント解説

この手法のポイントは、値と元のインデックスをペアにしてソートすることで、「ソート後の正しい位置」と「現在の位置」の対応関係を容易に把握できる点にあります。ある要素がまだ正しい位置になければ、その要素が指し示す相手と入れ替える操作を繰り返すことで、必ず正しい位置へ移動させることができます。

計算量はソート処理が支配的となるため O(n log n) です。また、このアルゴリズムは配列内の要素がすべて異なる(重複がない)ことを前提としている点にも注意してください。

  1. 【C++】配列を互いに素な配列に変換するための最小挿入回数を求める方法

    問題の概要 今回は、与えられた配列を互いに素な配列(コプライム配列)に変換するために必要な最小の挿入回数を求める、興味深い問題を取り上げます。互いに素な配列とは、隣り合う任意の2つの要素の最大公約数(GCD)が必ず1になる配列のことです。この記事では、必要な挿入回数に加えて、変換後の配列そのものも出力します。 例として、{5, 10, 20} という配列を考えてみましょう。この配列は隣接要素同士のGCDが5や10となるため、互いに素な配列ではありません。しかし、5と10の間、そして10と20の間にそれぞれ「1」を挿入すれば、{5, 1, 10, 1, 20} となり、すべての隣接ペアのGCD

  2. C++でカウントソート(計数ソート)を実装する方法

    カウントソートとは カウントソート(計数ソート)は安定なソート手法の一つで、小さな整数値をキーとするデータを並べ替えるために用いられるアルゴリズムです。キー値が同じ要素の個数を数え、その情報をもとに整列を行うのが大きな特徴です。キー同士の差(値の範囲)がそれほど大きくなければ非常に高い効率を発揮しますが、範囲が広すぎる場合は空間計算量が増大する点に注意が必要です。 カウントソートの計算量 時間計算量:O(n+r) 空間計算量:O(n+r) ※ n は要素数、r はキーの最大値(値の範囲)を表します。 入力: ソートされていないデータ列: 2 5 6 2 3 10 3 6 7 8出力: ソー