さて、前回に引き続いてソートの話です。
前回の終わりで、QuickSort は不安定なソートなので問題があるという話をしました。
では、どんなときに問題があるのかを説明します。
Number | Total |
2 | 12 |
3 | 14 |
1 | 15 |
2 | 22 |
1 | 32 |
2 | 37 |
3 | 39 |
1 | 49 |
2 | 52 |
3 | 56 |
1 | 64 |
3 | 68 |
まあ、こんなデータがあったとします。
この時に、Number でソートしてあって、同じ Number の行は Total でソートしてあって欲しいといわれたらどうでしょうか?
QuickSort で作った場合、これは出来ますか?
やろうと思ったら、Number でソートした後、同じ Number の列だけを Total でそれぞれソートをかけるなどというスマートでない手を使うことになります。
ところが、安定なソートの場合は Total でソートした後、Number でソートするだけで終わりです。
キーソートにおいては、安定なソートの方が便利なことが多いのです。
以下に安定なソートの代表格である MergeSort を使ったコード例を示します。
procedure TForm1.Sort(ACol: Integer);
procedure MergeSort(Buffer: TStringList; ARow, Count: Integer);
var
I, J: Integer;
Center: Integer;
Temp: TStringList;
begin
if Count = 1 then Exit;
Center := Count div 2;
MergeSort(Buffer, ARow, Center);
MergeSort(Buffer, ARow + Center, Count - Center);
I := 0;
J := 0;
Temp := TStringList.Create;
while (I < Center) and (J < Count - Center) do
begin
if CompareStr(Buffer[ARow + I], Buffer[ARow + Center + J]) > 0 then
begin
Temp.AddObject(Buffer[ARow + Center + J], Buffer.Objects[ARow + Center + J]);
Inc(J);
end
else
begin
Temp.AddObject(Buffer[ARow + I], Buffer.Objects[ARow + I]);
Inc(I);
end;
end;
if I = Center then
while J < Count - Center do
begin
Temp.AddObject(Buffer[ARow + Center + J], Buffer.Objects[ARow + Center + J]);
Inc(J);
end
else
while I < Center do
begin
Temp.AddObject(Buffer[ARow + I], Buffer.Objects[ARow + I]);
Inc(I);
end;
for I := 0 to Count - 1 do
begin
Buffer[ARow + I] := Temp[I];
Buffer.Objects[ARow + I] := Temp.Objects[I];
end;
Temp.Free;
end;
var
ARow: Integer;
Buffer: TStringList;
begin
with StringGrid1 do
begin
Buffer := TStringList.Create;
//Buffer に key とそれに対応する Rows を格納する
for ARow := TopRow to RowCount - 1 do
begin
Buffer.AddObject(Cells[ACol, ARow], TStringList.Create);
TStringList(Buffer.Objects[ARow - TopRow]).Assign(Rows[ARow]);
end;
//Buffer を実際にソートする
MergeSort(Buffer, 0, RowCount - TopRow); //Buffer.Sort; と置き換え
//ソートしたデータを Grid に書き戻す
for ARow := TopRow to RowCount - 1 do
begin
Rows[ARow].Assign(TStringList(Buffer.Objects[ARow - TopRow]));
TStringList(Buffer.Objects[ARow - TopRow]).Free;
end;
Buffer.Free;
end;
end;
基本的に、前のソースのソート部分をマージソートに置き換えただけです。
マージソート部分のアルゴリズムは、アルゴリズム本にいくらでも載っているのでそれを見てください。
ただ、マージソート部分のソースコードは自分的には汚いように見えて気に食いません。
誰かきれいに書けたという方がいましたら、コードを送ってください。
私もきれいだと思ったら、名前入りで(匿名も可)ここのソースと差し替えさせていただきます。