このドキュメントは http://edu.net.c.dendai.ac.jp/ 上で公開されています。
順番にデータを並び替えることをソートと言います。 今回はソートのアルゴリズムを紹介します。 始めに前回示した順序木によるソートの分析をし、そのあと、配列変数に対す るソートのアルゴリズムを示します。
プログラムの効率を評価する時、入力の長さを n とした時にプログラムが 実行するステップ数を関数の形で計算します。 そして、関数が複数の項の足し算になっている時、もっとも増え方が速い項を 取り出し、係数を除いたものを考えます。 例えば、次のプログラムの効率を評価します。
#include <stdio.h>
main(){
int i,j,s;
scanf("%d",&i);
s=0;
for(j=1;j<=i;j++){
s+=j;
}
printf("%d\n",s);
}
すると全部の手間は 1+1+n+1+(1+2n(n+2n+n))+2n = 2n・4n + 3n + 3 となります。 この中でもっとも増え方の速い項は 2n・4n です。そして、係数を取り除くと 2n・n と なります。 この時、このプログラムの実行時間はオーダー2n・n だと言います。 式で書くと O(2n・n)となります。 プログラムの計算時間の評価はこのように関数の形で行います。 どうしてこのような雑な関数の取り扱いになるかは 1部データ構造とアルゴリズム第6回アルゴ リズムに関する数学 3. 計算量を参照して下さい(Internet Explorer ver.6 では見られません)。
前回示した順序木の処理の計算量を求めます。 深さ k の木にはおおよそ 2k個のデータを詰め込めます。従って、 データ数が n 個の順序木の深さはおおよそ log n になります。
add 関数は新しく頂点を付け加える作業をします。 そのためには木の端まで木をたどり、そこに頂点を新しくつくって接続します。 木の端までたどるには木の深さ程度の手間 O(log n) 程度かかります。 新しく頂点を作る作業はO(1)程度かかるとします。 従って、add 関数一回の手間は O(log n)です。 これをデータ数 n だけ繰り返す必要があるので 与えられたデータから順序木を作るには O( n log n ) の手間がかかります。
一方 show 関数は全データを表示するだけですから O(n) の手間がかかります。
従って順序木を使用した並べ替えにかかる手間は O(n log n + n ) = O(n log n) となります。
これから示すのは良く知られている効率のわるい並び替えアルゴリズムです。 これらをレポートに使用してはいけません。 なおこれからは配列変数に対してソートを行うことを考えます。
配列に対して、順序木を使ったソートに匹敵する効率でソートするにはどうす ればいいでしょうか? そのためには、順序木にデータを入れるようにデータを動かす必要があります。 順序木の性質により根の頂点に入るデータに対して、左に接続する木に含まれ るデータは全て値が小さく、右に接続する木に含まれるデータは全て値が大き くなります。 そこで、配列に対して、根に入る値、左側に入る値、右側に入る値を分類する ことを考えます。 この作業を行う関数を partition と呼ぶことにします。 すると、左側に入る値に対しても partition 、右側に入る値に対しても partition を行うことにより最終的に全てのデータを順序木のどこに配置すれ ば良いかを決めることができます。
配列の中身をソートするには、左側に入る値を前に、右側に入る値を後ろに移 動させる必要があります。 そして、左側に入る値を前半部分、右側に入る値を後半部分に移動させられれ ば、こんどはその部分に対して partition を実行させます。 従って、 partition の引数にはどこからどこまでを対象にするかを与える必 要がありますので、関数宣言は次のようになります。
void partition(int s, int t);
そして、内部では次のようにします。
このようにすると O( n log n ) でデータをソートできます。 このようなソートをクイックソートと言います。
大きい順にソートするプログラムを書き、上記のデータをソートし出力させな さい。