記事のディレクトリ
このチュートリアルでは、マージソートアルゴリズムについて学習します。さらに、C言語でのマージソートの例があります。
マージソートは、分割統治アルゴリズムの原理に基づく最も一般的なソートアルゴリズムの1つです。
ここで、問題は複数のサブ問題に分けられます。各サブ問題は個別に解決されます。最後に、サブ問題を組み合わせて最終的な解決策を形成します。
分割統治戦略
分割統治法を使用して、問題をサブ問題に分割します。各サブ問題の解決策の準備ができたら、サブ問題の結果を「結合」して、主要な問題を解決します。
配列Aを並べ替える必要があるとします。サブ問題は、この配列のサブパーツを、インデックスpから始まり、A [p ... r]で示されるインデックスrで終わるように並べ替えることです。
分割
qがpとrの中間点である場合、サブ配列A [p ... r]を2つの配列A [p ... q]とA [q + 1、r]に分割できます。
処理
処理ステップでは、サブ配列A [p ... q]とA [q + 1、r]を並べ替えようとします。基本レベルにまだ分割していない場合は、2つのサブ配列を再度分割して、並べ替えを試みます。
組み合わせと
処理のステップの後、2つのソートされたサブ配列A [p ... q]とA [q + 1、r]を取得します。これら2つの配列を結合した後、ソートされた配列A [p ... rを取得します。 ]。
マージソートアルゴリズム
マージソート関数は、サイズ1のサブ配列(p == r)でマージソートを実行しようとする段階に達するまで、配列を繰り返し半分に分割します。
その後、マージ関数が機能し、配列全体がマージされるまで、ソートされた配列をより大きな配列にマージします。
MergeSort(A, p, r):
if p > r
return
q = (p+r)/2
mergeSort(A, p, q)
mergeSort(A, q+1, r)
merge(A, p, q, r)
配列全体をソートするには、MergeSort(A、0、length(A)-1)を呼び出す必要があります。
次の図に示すように、マージソートアルゴリズムは、1つの要素を含む配列の基本的な状況が得られるまで、配列を再帰的に半分に分割します。その後、マージ関数はソートされたサブ配列を選択し、それらをマージして配列全体を徐々にソートします。
マージソートのマージステップ
すべての再帰的アルゴリズムは、基本的な状況と、基本的な状況の結果を組み合わせる機能に依存しています。マージソートについても同じことが言えます。マージソートアルゴリズムの最も重要な部分は、マージステップです。
マージステップは、2つのソートされたリスト(配列)を1つの大きなソートされたリスト(配列)にマージするという単純な問題を解決します。
アルゴリズムは3つのポインターを維持します。1つは2つの配列のそれぞれに対応し、もう1つは最終的にソートされた配列の現在のインデックスを維持するために使用します。
我们已经到达任何阵列的末端了吗?
否:
比较两个数组的当前元素
将较小的元素复制到排序的数组中
移动包含较小元素的元素的指针
是:
复制非空数组的所有剩余元素
マージアルゴリズムのコードを書く
上記のマージステップとマージソートに使用されるマージステップの明らかな違いの1つは、連続するサブ配列に対してのみマージ機能を実行することです。
これが、配列、最初の位置、最初のサブ配列の最後のインデックス(2番目のサブ配列の最初のインデックスを計算できます)、および2番目のサブ配列の最後のインデックスのみが必要な理由です。
私たちのタスクは、2つのサブ配列A [p ... q]とA [q + 1 ... r]をマージして、ソートされた配列A [p ... r]を作成することです。したがって、関数の入力はA、p、q、およびrです。
マージ関数の動作原理は次のとおりです。
- サブ配列L←A [p…q]およびM←A [q + 1…r]のコピーを作成します。
- 3つのポインタi、j、およびk
aを作成します。1から開始して、iは現在のインデックスをL
bとして保持します。1から開始して、jは現在のインデックスをM
cとして保持します。pから開始して、kは現在のインデックスをA [p…として保持します。 Q]。 - LまたはMの終わりに到達する前に、LおよびMから大きい要素を選択し、それらをA [p ... q]の正しい位置に配置します。
- LまたはMの要素が使い果たされたら、残りの要素を選択してA [p ... q]に入れます。
コードでは、これは次のようになります。
// Merge two subarrays L and M into arr
void merge(int arr[], int p, int q, int r) {
// Create L ← A[p..q] and M ← A[q+1..r]
int n1 = q - p + 1;
int n2 = r - q;
int L[n1], M[n2];
for (int i = 0; i < n1; i++)
L[i] = arr[p + i];
for (int j = 0; j < n2; j++)
M[j] = arr[q + 1 + j];
// Maintain current index of sub-arrays and main array
int i, j, k;
i = 0;
j = 0;
k = p;
// Until we reach either end of either L or M, pick larger among
// elements L and M and place them in the correct position at A[p..r]
while (i < n1 && j < n2) {
if (L[i] <= M[j]) {
arr[k] = L[i];
i++;
} else {
arr[k] = M[j];
j++;
}
k++;
}
// When we run out of elements in either L or M,
// pick up the remaining elements and put in A[p..r]
while (i < n1) {
arr[k] = L[i];
i++;
k++;
}
while (j < n2) {
arr[k] = M[j];
j++;
k++;
}
}
Merge()関数が段階的に説明されています
この関数には多くのことが起こるので、例を見て、どのように機能するかを見てみましょう。
配列A [0…5]には、2つのソートされたサブ配列A [0…3]とA [4…5]が含まれています。マージ関数が2つの配列をマージする方法を見てみましょう。
void merge(int arr[], int p, int q, int r) {
// Here, p = 0, q = 4, r = 6 (size of array)
ステップ1:ソートするサブアレイのコピーを作成する
// Create L ← A[p..q] and M ← A[q+1..r]
int n1 = q - p + 1 = 3 - 0 + 1 = 4;
int n2 = r - q = 5 - 3 = 2;
int L[4], M[2];
for (int i = 0; i < 4; i++)
L[i] = arr[p + i];
// L[0,1,2,3] = A[0,1,2,3] = [1,5,10,12]
for (int j = 0; j < 2; j++)
M[j] = arr[q + 1 + j];
// M[0,1,2,3] = A[4,5] = [6,9]
ステップ2:サブアレイとメインアレイの現在のインデックスを維持する
int i, j, k;
i = 0;
j = 0;
k = p;
ステップ3:LまたはMの終わりに到達する前に、要素LおよびMから大きい方の要素を選択し、それらをA [p ... r]の正しい位置に配置します。
while (i < n1 && j < n2) {
if (L[i] <= M[j]) {
arr[k] = L[i]; i++;
}
else {
arr[k] = M[j];
j++;
}
k++;
}
ステップ4:LまたはMの要素が使い果たされたら、残りの要素を選択して[p ... r]に入力します。
// We exited the earlier loop because j < n2 doesn't hold
while (i < n1)
{
arr[k] = L[i];
i++;
k++;
}
// We exited the earlier loop because i < n1 doesn't hold
while (j < n2)
{
arr[k] = M[j];
j++;
k++;
}
}
Mの値がLより大きい場合は、この手順が必要です。
マージ関数の最後に、サブ配列A [p ... r]がソートされます。
Cの例
// Merge sort in C
#include <stdio.h>
// Merge two subarrays L and M into arr
void merge(int arr[], int p, int q, int r) {
// Create L ← A[p..q] and M ← A[q+1..r]
int n1 = q - p + 1;
int n2 = r - q;
int L[n1], M[n2];
for (int i = 0; i < n1; i++)
L[i] = arr[p + i];
for (int j = 0; j < n2; j++)
M[j] = arr[q + 1 + j];
// Maintain current index of sub-arrays and main array
int i, j, k;
i = 0;
j = 0;
k = p;
// Until we reach either end of either L or M, pick larger among
// elements L and M and place them in the correct position at A[p..r]
while (i < n1 && j < n2) {
if (L[i] <= M[j]) {
arr[k] = L[i];
i++;
} else {
arr[k] = M[j];
j++;
}
k++;
}
// When we run out of elements in either L or M,
// pick up the remaining elements and put in A[p..r]
while (i < n1) {
arr[k] = L[i];
i++;
k++;
}
while (j < n2) {
arr[k] = M[j];
j++;
k++;
}
}
// Divide the array into two subarrays, sort them and merge them
void mergeSort(int arr[], int l, int r) {
if (l < r) {
// m is the point where the array is divided into two subarrays
int m = l + (r - l) / 2;
mergeSort(arr, l, m);
mergeSort(arr, m + 1, r);
// Merge the sorted subarrays
merge(arr, l, m, r);
}
}
// Print the array
void printArray(int arr[], int size) {
for (int i = 0; i < size; i++)
printf("%d ", arr[i]);
printf("\n");
}
// Driver program
int main() {
int arr[] = {
6, 5, 12, 10, 9, 1};
int size = sizeof(arr) / sizeof(arr[0]);
mergeSort(arr, 0, size - 1);
printf("Sorted array: \n");
printArray(arr, size);
}
マージソートの複雑さ
時間計算量
ベストケースの複雑さ:O(n * log n)
ワーストケースの複雑さ:O(n * log n)
平均ケースの複雑さ:O(n * log n)
スペースの複雑さ
マージソートのスペースの複雑さはO(n)です。
マージソートアプリケーション
- カウントダウンの問題
- 外部分類
- Eコマースアプリケーション
参照文書
[1]パレワラボPvt。Ltd.マージソートアルゴリズム[EB / OL] .https://www.programiz.com/dsa/merge-sort,2020-01-01。