マージソートは整列されていないリストを2つのリストに分割して、それぞれを整列させた後、それらをマージして整列済みのひとつのリストを作ります

マージを利用してリストを整列するのでマージソートという名がついています。

アルゴリズム分析

  1. 整列されていないリストを2つのサブリストに分割する
  2. サブリストを整列する
  3. サブリストをマージしてひとつの整列済みリストにする

分割された部分的なリストをサブリストと呼びます。サブリストもマージソートを使って整列させます。2. の手順にはこのアルゴリズムを再帰的に適用することになります。

図解

リストをいったんバラバラにしたあと、マージによってリストを再構築します。

マージソートの図解

マージ(併合)

マージとはいくつかの整列済みのリストをひとつのリストにまとめる操作です。

マージの手順

整列済みのリストAとリストBをマージして整列されたリストCを作ると考えます。

  1. リストAとリストBの先頭の要素を比較して、小さい方をリストから削除してリストCの末尾に追加する
  2. リストA、リストB、どちらか一方の要素がなくなるまで 1. を繰り返す
  3. 残った要素をリストCの末尾に順に追加する

以上の手順でマージは完了します。

サンプルコード

C言語

配列を昇順に整列させます。

void mergeSort(int numbers[], int temp[], int array_size)
{
  m_sort(numbers, temp, 0, array_size - 1);
}

void m_sort(int numbers[], int temp[], int left, int right)
{
  int mid;

  if (right > left)
  {
    mid = (right + left) / 2; /* 配列を分割する位置 */
    m_sort(numbers, temp, left, mid);
    m_sort(numbers, temp, mid+1, right);

    merge(numbers, temp, left, mid+1, right);
  }
}

void merge(int numbers[], int temp[], int left, int mid, int right)
{
  int i, left_end, num_elements, tmp_pos;

  left_end = mid - 1;
  tmp_pos = left;
  num_elements = right - left + 1; /* 配列の要素数 */

  /* 2つのリストに要素が残っている */
  while ((left <= left_end) && (mid <= right))
  {
    if (numbers[left] <= numbers[mid])
    {
      temp[tmp_pos] = numbers[left];
      tmp_pos = tmp_pos + 1;
      left = left +1;
    }
    else
    {
      temp[tmp_pos] = numbers[mid];
      tmp_pos = tmp_pos + 1;
      mid = mid + 1;
    }
  }

  /* 左側のリスト */
  while (left <= left_end)
  {
    temp[tmp_pos] = numbers[left];
    left = left + 1;
    tmp_pos = tmp_pos + 1;
  }
  /* 右側のリスト */
  while (mid <= right)
  {
    temp[tmp_pos] = numbers[mid];
    mid = mid + 1;
    tmp_pos = tmp_pos + 1;
  }

  /* 昇順に整列するようひとつのリストにまとめる */
  for (i=0; i <= num_elements; i++)
  {
    numbers[right] = temp[right];
    right = right - 1;
  }
}

マージソートのデモンストレーション プログラム: merge_sort.c

Python

def merge_sort(aList):
    if len(aList) <= 1:
        return aList

    mid = len(aList) // 2
    left = aList[:mid]
    right = aList[mid:]

    left = merge_sort(left)
    right = merge_sort(right)

    return list(merge(left, right))


def merge(left, right):
    sorted_list = []
    left_index = 0
    right_index = 0

    while left_index < len(left) and right_index < len(right):
        if left[left_index] <= right[right_index]:
            sorted_list.append(left[left_index])
            left_index += 1
        else:
            sorted_list.append(right[right_index])
            right_index += 1

    if left:
        sorted_list.extend(left[left_index:])
    if right:
        sorted_list.extend(right[right_index:])

    return sorted_list


if __name__ == '__main__':
    from random import shuffle
    l = range(15)
    lcopy = l[:]
    shuffle(l)
    print('Unsorted')
    print l
    assert l != lcopy
    print ('Sorted')
    l = merge_sort(l)
    print l
    assert l == lcopy