AIZU ONLINE JUDGE

AOJ ALDS1 5_B(Merge Sort)を解く

AOJ_ALDS1_5_B

Aizu Online Judge(AOJ)が提供している「アルゴリズムとデータ構造入門」(ALDS1)の5_B問題をC++で解いてみました。

ALDS1では、「アルゴリズムとデータ構造の基礎を体得します。」とあります。トピック#5「分割統治法」では、部分的な小さな問題を解くことによって、与えられた元の問題を解く手法を紹介します。

問題(5_B: Merge Sort)

問題はリンク先をご覧ください。

AOJ ALDS1 5_B問題: Merge Sort

マージソートについて学びます。

マージソートについて

全体を2つに分割して、それぞれをソートして、その結果を小さな要素から取り出してソートした結果を得ます。分割は再帰的に行います。

計算量は、$O(N \log N)$ で高速に動きますが、別にメモリを確保する必要があります。

以下が擬似コードとなります。mergeSort を呼び出すと、再帰的に mergeSort が呼び出されて、最後に merge します。

merge(A, left, mid, right)
    n1 = mid - left;
    n2 = right - mid;
    L[0...n1], R[0...n2] を生成
    for i = 0 to n1-1
        L[i] = A[left + i]
    for i = 0 to n2-1
        R[i] = A[mid + i]
    L[n1] = INFTY
    R[n2] = INFTY
    i = 0
    j = 0
    for k = left to right-1
       if L[i] <= R[j]
            A[k] = L[i]
            i = i + 1
       else 
            A[k] = R[j]
            j = j + 1

mergeSort(A, left, right)
    if left+1 < right
         mid = (left + right)/2;
         mergeSort(A, left, mid)
         mergeSort(A, mid, right)
         merge(A, left, mid, right)

マージソートの特徴は以下です。

  • 計算量は、$O(N \log N)$
  • 安定なソート(stable sort)
  • 標準ライブラリ stable_sort では、マージソートで実装される場合が多い。

C++ プログラム例(ALDS1 5_B)

擬似コードをそのままC++にしています。門番に使う INFTY は、(1 << 30) としました(5行目)。

以下が、最終的なC++プログラムとなります。

#include <iostream>
#include <vector>
using namespace std;

#define INFTY (1 << 30)
vector<int> L;
vector<int> R;
int cnt = 0;

void merge(vector<int>& A, int left, int mid, int right)
{
	int n1 = mid - left;
	int n2 = right - mid;
	for (int i = 0; i < n1; ++i) {
		L[i] = A[left + i];
	}
	for (int i = 0; i < n2; ++i) {
		R[i] = A[mid + i];
	}
	L[n1] = INFTY;
	R[n2] = INFTY;
	int i = 0;
	int j = 0;
	for (int k = left; k < right; ++k) {
		++cnt;
		if (L[i] <= R[j]) {
			A[k] = L[i++];
		} else {
			A[k] = R[j++];
		}
	}
}

void mergeSort(vector<int>& A, int left, int right)
{
	if (left + 1 < right) {
		int mid = (left + right) / 2;
		mergeSort(A, left, mid);
		mergeSort(A, mid, right);
		merge(A, left, mid, right);
	}
}

int main()
{
	int n;
	cin >> n;
	vector<int> s(n);
	L.assign(n / 2 + 2, 0);
	R.assign(n / 2 + 2, 0);
	for (int i = 0; i < n; ++i) {
		cin >> s[i];
	}

	mergeSort(s, 0, n);

	for (int i = 0; i < n; ++i) {
		cout << s[i] << " \n"[i == n - 1];
	}
	cout << cnt << endl;

	return 0;
}

上記プログラムは、AOJ で「AC(Accepted=正解)」と判定されました。

最後に

計算量 $O(N^2)$ の挿入ソート/バブルソート/選択ソートを紹介しました。マージソートは、計算量 $O(N \log N)$ でソートできます。分割して計算を行う威力が分かります。

引き続き、ALDS1 の問題を紹介していきます。

COMMENT

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA