目次

キーワード

概要

詳しくは「優先度付き待ち行列」で説明しますが、 ヒープというのは、 常に最大の要素を取り出せる状態に保たれているデータ構造です。

常に最大の要素を取り出せるなら、当然それをソートアルゴリズムに転用できます。 ということで、 そのヒープ構造を使ったソートアルゴリズムをヒープソート(heap sort)と言います。 「不安定」な「内部」ソート。

しかしながら、平均的なケースにおいては、クイックソートの方が高速ですが、 平均計算量も最悪計算量もどちらも O(n log n) となり、 常に安定して高速です。 この点に関しては「クイックソート」よりも優れています。

ただし、クイックソートも、最悪のケースに陥らないような改良策がいろいろ考えられているので、 ヒープソートの方がクイックソート(の改良版)よりも高速になる場面はそれほどありません。

サンプルソース

https://github.com/ufcpp/UfcppSample/blob/master/Chapters/Algorithm/Sort/HeapSort.cs

/// <summary>
/// ヒープソート。
/// </summary>
/// <param name="a">対象の配列</param>
public static void HeapSort<T>(T[] a)
  where T : IComparable<T>
{
  for (int i = 1; i < a.Length; ++i)
    MakeHeap(a, i);
  for (int i = a.Length - 1; i >= 0; --i)
    a[i] = PopHeap(a, i);
}

/// <summary>
/// 配列をヒープ化する。
/// n - 1 番目までの要素は既にヒープ化されていることを仮定して、
/// n 番目の要素をヒープに追加。
/// </summary>
/// <param name="a">対象の配列</param>
/// <param name="n">要素数</param>
static void MakeHeap<T>(T[] a, int n)
  where T : IComparable<T>
{
  while (n != 0)
  {
    int i = (n - 1) / 2;
    if (a[n].CompareTo(a[i]) > 0) Swap(ref a[n], ref a[i]);
    n = i;
  }
}

/// <summary>
/// ヒープから最大値を取り出す。
/// </summary>
/// <param name="a">対象の配列</param>
/// <param name="n">要素数 - 1</param>
/// <returns>取り出した最大値</returns>
static T PopHeap<T>(T[] a, int n)
  where T : IComparable<T>
{
  T max = a[0];

  a[0] = a[n];

  for (int i=0, j; (j = 2 * i + 1) < n; )
  {
    if ((j != n - 1) && (a[j].CompareTo(a[j + 1]) < 0)) j++;
    if (a[i].CompareTo(a[j]) < 0) Swap(ref a[i], ref a[j]);
    i = j;
  }

  return max;
}

更新履歴

ブログ