using System;
using System.Collections.Generic;
static class Sort
{
#region common
///
/// a と b の中身を入れ替える。
///
/// オペランドa
/// オペランドb
public static void Swap(ref T a, ref T b)
{
T c = a; a = b; b = c;
}
#endregion
#region O(n^2) ソート
///
/// バブルソート。
///
/// 対象の配列
public static void BubbleSort(T[] a)
where T : IComparable
{
int n = a.Length;
for (int i = 0; i < n - 1; i++)
for (int j = n - 1; j > i; j--)
if (a[j].CompareTo(a[j - 1]) < 0)
Swap(ref a[j], ref a[j - 1]);
}
///
/// 選択ソート。
///
/// 対象の配列
public static void SelectSort(T[] a)
where T : IComparable
{
int n = a.Length;
for (int i = 0; i < n; i++)
for (int j = 1; j < n - i; j++)
if (a[j - 1].CompareTo(a[j]) > 0)
Swap(ref a[j - 1], ref a[j]);
}
///
/// 挿入ソート。
///
/// 対象の配列
public static void InsertSort(T[] a)
where T : IComparable
{
int n = a.Length;
for (int i = 1; i < n; i++)
for (int j = i; j >= 1 && a[j - 1].CompareTo(a[j]) > 0; --j )
Swap(ref a[j], ref a[j - 1]);
}
///
/// シェルソート。
///
/// 対象の配列
public static void ShellSort(T[] a)
where T : IComparable
{
int n = a.Length;
int h;
for (h = 1; h < n / 9; h = h * 3 + 1) ;
for (; h > 0; h /= 3)
for (int i = h; i < n; i++)
for (int j = i; j >= h && a[j - h].CompareTo(a[j]) > 0; j -= h)
Swap(ref a[j], ref a[j - h]);
}
#endregion
#region クイックソート・マージソート準備
///
/// クイックソートやマージソート → 挿入ソートに切り替える配列長の閾値。
///
const int THREASHOLD = 64;
///
/// 挿入ソート。
/// 配列のどこからどこまでをソートするかを指定するバージョン。
///
/// 対象の配列
/// ソート対象の先頭インデックス
/// ソート対象の末尾インデックス
static void InsertSort(T[] a, int first, int last)
where T : IComparable
{
for (int i = first + 1; i <= last; i++)
for (int j = i; j > first && a[j - 1].CompareTo(a[j]) > 0; --j)
Swap(ref a[j], ref a[j - 1]);
}
#endregion
#region クイックソート
///
/// クイックソート。
///
/// 対象の配列
public static void QuickSort(T[] a)
where T : IComparable
{
QuickSort(a, 0, a.Length - 1);
}
///
/// クイックソート本体。
/// 配列のどこからどこまでをソートするかを指定するバージョン。
///
/// 対象の配列
/// ソート対象の先頭インデックス
/// ソート対象の末尾インデックス
static void QuickSort(T[] a, int first, int last)
where T : IComparable
{
// 要素数が少なくなってきたら挿入ソートに切り替え
if (last - first < THREASHOLD)
{
InsertSort(a, first, last);
return;
}
// 枢軸決定(配列の先頭、ど真ん中、末尾の3つの値の中央値を使用。)
T pivot = Median(a[first], a[(first + last) / 2], a[last]);
// 左右分割
int l = first;
int r = last;
while(l <= r)
{
while (l < last && a[l].CompareTo(pivot) < 0) l++;
while (r > first && a[r].CompareTo(pivot) >= 0) r--;
if (l > r) break;
Swap(ref a[l], ref a[r]);
l++; r--;
}
// 再帰呼び出し
QuickSort(a, first, l-1);
QuickSort(a, l, last);
}
///
/// 3つの値の中央値を求める。
///
/// オペランドa
/// オペランドb
/// オペランドc
/// 中央値
static T Median(T a, T b, T c)
where T : IComparable
{
if (a.CompareTo(b) > 0) Swap(ref a, ref b);
if (a.CompareTo(c) > 0) Swap(ref a, ref c);
if (b.CompareTo(c) > 0) Swap(ref b, ref c);
return b;
}
#endregion
#region ヒープソート
///
/// ヒープソート。
///
/// 対象の配列
public static void HeapSort(T[] a)
where T : IComparable
{
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);
}
///
/// 配列をヒープ化する。
/// n - 1 番目までの要素は既にヒープ化されていることを仮定して、
/// n 番目の要素をヒープに追加。
///
/// 対象の配列
/// 要素数
static void MakeHeap(T[] a, int n)
where T : IComparable
{
while (n != 0)
{
int i = (n - 1) / 2;
if (a[n].CompareTo(a[i]) > 0) Swap(ref a[n], ref a[i]);
n = i;
}
}
///
/// ヒープから最大値を取り出す。
///
/// 対象の配列
/// 要素数 - 1
/// 取り出した最大値
static T PopHeap(T[] a, int n)
where T : IComparable
{
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;
}
#endregion
#region マージソート
///
/// マージソート。
///
/// 対象の配列
public static void MergeSort(T[] a)
where T : IComparable
{
T[] work = new T[a.Length / 2];
MergeSort(a, 0, a.Length, work);
}
///
/// マージソート。
/// 配列のどこからどこまでをソートするかを指定するバージョン。
///
/// 対象の配列
/// ソート対象部分の先頭
/// ソート対象部分の末尾+1
/// 作業領域。a の 1/2 のサイズが必要。
static void MergeSort(T[] a, int begin, int end, T[] work)
where T : IComparable
{
if (end - begin < THREASHOLD)
{
InsertSort(a, begin, end - 1);
return;
}
int mid = (begin + end) / 2;
MergeSort(a, begin, mid, work);
MergeSort(a, mid, end, work);
Merge(a, begin, mid, end, work);
}
///
/// 配列 a の、[begin, mid) の部分と [mid, end) の部分をマージ。
///
///
/// マージ対象の配列
/// aの先頭
/// aの分割点
/// aの末尾+1
/// 作業領域
static void Merge(T[] a, int begin, int mid, int end, T[] work)
where T : IComparable
{
int i, j, k;
for (i = begin, j = 0; i != mid; ++i, ++j) work[j] = a[i];
mid -= begin;
for (j = 0, k = begin; i != end && j != mid; ++k)
{
if (a[i].CompareTo(work[j]) < 0)
{
a[k] = a[i];
++i;
}
else
{
a[k] = work[j];
++j;
}
}
for (; i < end; ++i, ++k) a[k] = a[i];
for (; j < mid; ++j, ++k) a[k] = work[j];
}
#endregion
#region バケットソート
///
/// [0, max] の範囲の整数をバケットソート。
///
/// 対象の配列
/// 配列 a 中の最大値
public static void BucketSort(int[] a, int max)
{
// バケツを用意
int[] bucket = new int[max + 1];
// バケツに値を入れる
for (int i = 0; i < a.Length; ++i) ++bucket[a[i]];
// バケツ中の値の結合
for (int j = 0, i = 0; j < bucket.Length; ++j)
for (int k = bucket[j]; k != 0; --k, ++i)
a[i] = j;
}
///
/// [0, max] の範囲の整数をキーに持つデータ構造をバケットソート。
///
/// 値の型
/// 対象の配列
/// キーの最大値
public static void BucketSort(KeyValuePair[] a, int max)
{
// バケツを用意
List[] bucket = new List[max + 1];
// バケツに値を入れる
for (int i = 0; i < a.Length; ++i)
{
if (bucket[a[i].Key] == null) bucket[a[i].Key] = new List();
bucket[a[i].Key].Add(a[i].Value);
}
// バケツ中の値の結合
for (int j = 0, i = 0; j < bucket.Length; ++j)
if(bucket[j] != null)
foreach (T val in bucket[j])
a[i++] = new KeyValuePair(j, val);
}
#endregion
#region 基数ソート
///
/// 基数ソート。
/// 概念説明用の簡易版。
/// 10進数で3桁(0~999)までしかソートできない。
///
/// 対象の配列
/// 配列 a 中の最大値
public static void RadixSort10(int[] a)
{
// バケツを用意
List[] bucket = new List[10];
for (int d = 0, r = 1; d < 3; ++d, r *= 10)
{
// バケツに値を入れる
for (int i = 0; i < a.Length; ++i)
{
int key = (a[i] / r) % 10; // a[i] の d 桁目だけを取り出す。
if (bucket[key] == null) bucket[key] = new List();
bucket[key].Add(a[i]);
}
// バケツ中の値の結合
for (int j = 0, i = 0; j < bucket.Length; ++j)
if (bucket[j] != null)
foreach (int val in bucket[j])
a[i++] = val;
// バケツを一度空にする
for (int j = 0; j < bucket.Length; ++j)
bucket[j] = null;
}
}
///
/// 基数ソート。
///
/// 対象の配列
/// 配列 a 中の最大値
public static void RadixSort(int[] a)
{
// バケツを用意
List[] bucket = new List[256];
for (int d = 0, logR = 0; d < 4; ++d, logR += 8)
{
// バケツに値を入れる
for (int i = 0; i < a.Length; ++i)
{
int key = (a[i] >> logR) & 255; // a[i] を256進 d 桁目だけを取り出す。
if (bucket[key] == null) bucket[key] = new List();
bucket[key].Add(a[i]);
}
// バケツ中の値の結合
for (int j = 0, i = 0; j < bucket.Length; ++j)
if (bucket[j] != null)
foreach (int val in bucket[j])
a[i++] = val;
// バケツを一度空にする
for (int j = 0; j < bucket.Length; ++j)
bucket[j] = null;
}
}
#endregion
}