最近、このブログに「バブルソート」とか具体的なソート名の検索で見てくださっている方がいらっしゃるようだ。
なので、単体で解説をしていこう。
今回は、単純なソートアルゴリズムの一つである「バブルソート」をご紹介する。
基本的なものなので、何をしているか、しっかり把握してほしい。
分からなくなったら、
一個ずつ慎重に処理を追っていこう!
単純なソートアルゴリズムの一つ「バブルソート」
これは、二つの隣接したデータを比較しながら、逆順であれば入れ替えるということを繰り返すアルゴリズムだ。
先に、アルゴリズム全体を。
入力はdata
配列、添え字0~n-1までのn要素を持つとしよう。
出力もdata
配列で、中身はソート済みとする。
この場合のバブルソートアルゴリズムは、以下の通りだ。
i
を0
からn-2
まで1つずつ増やしながら、以下を繰り返すj
をn-1
からi+1
まで1つずつ減らしながら、以下を繰り返すdata[j - 1] > data[j]
なら、この2つを入れ替える
data
を出力する
これだけだと分かりづらいので、具体的な数値で見てみよう。
以下のような6個の要素を持つ配列をソートしていく。
i | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
data[i] | 5 | 3 | 4 | 1 | 2 | 6 |
では、まず変数i
を用意し、0
から見ていく。
ここで、j=n-1=5
とし、更に次に進もう。
data[j-1]=data[4]
と、data[j]=data[5]
を比較する。
今、2と6なので、data[4] < data[5]
となっているからそのままだ。
次に、j
を1減らして、また見ていく。
今度も、1と2でdata[3] < data[4]
となっている、変化なし。
次に、やっと変化が起こる。
再度j
を1減らし、今j
は3だ。
つまり、data[2]
とdata[3]
を比較する。
data[2] = 4
、data[3] = 1
なので、今度はdata[2] > data[3]
だ。
よって、この二つを入れ替えよう。
i | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
data[i] | 5 | 3 | 1 | 4 | 2 | 6 |
この状態で、更に次へ。
data[1] > data[2]なので、ここも入れ替え。
i | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
data[i] | 5 | 1 | 3 | 4 | 2 | 6 |
最後、data[0] > data[1]なので、同じく入れ替える。
i | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
data[i] | 1 | 5 | 3 | 4 | 2 | 6 |
このとき、j=1
でi=0
、つまりj=i+1
となったので、i
の一回目のループが終了となる。
さて、このときのデータに注目してみよう。
先頭に、一番小さいデータ1が来ているのが分かるだろうか。
つまり、1回のループで、先頭の要素がソート済みとなる。
このように、小さい数が上がってくることから、バブル(泡)のようなのでバブルソートと呼ぶのだ。
あとは、範囲を一個ずつ狭めて、同じことを最後までしてあげればいい。
参考までに、i
のループが終わるごとの途中経過を出しておこう。
太字がすでにソート済みの範囲、赤字が新たにソートされた箇所だ。
i | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
data[i] | 1 | 2 | 5 | 3 | 4 | 6 |
i | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
data[i] | 1 | 2 | 3 | 5 | 4 | 6 |
i | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
data[i] | 1 | 2 | 3 | 4 | 5 | 6 |
i | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
data[i] | 1 | 2 | 3 | 4 | 5 | 6 |
これで、n-1個の並び替えが終わった状態だ。
実は、この時点でソート完了となる。
というのも、最後に残っているのは1個なので、それもソート済みと見なせるのだ。
以上が、バブルソートの流れだ。
なお、このバブルソートは、安定なソートだ。
安定とは、並び替えるときに同じキーがあった場合に、元の順番を保持するかどうかという考え方。
安定ならば、元の順番が保持される。
安定でないならば、その箇所の並びについては保証されない。
例えば、並び替えるときに、5という数字が二つ出ていたとしよう。
このとき、安定ならば、元の5の出てくる順番でソートがされる。
しかし、安定でないなら、この5は前後どちらになるか分からないのだ。
この、安定である必要があるかどうかも重要な確認項目なので、条件を忘れずにチェックしてほしい。
バブルソートの計算量
次に、バブルソートの計算量を見ていこう。
先に、心配な方は計算量も解説しているので、そちらをご参照頂きたい。
計算量とは?プログラムの性能を計ってみよう | Shino’s Mind Archive
まず、計算回数を全部足してみる。
データ数をnとすると、1回目のiのループでn-1回、2回目でn-2回、…、n-1回目で1回と計算をしている。
つまり、比較の合計は、
$$(n – 1) + (n – 2) + … + 2 + 1 = \frac{n(n – 1)}{2}$$
と計算できる。
次に、条件に合致したら入れ替える処理だが…これは定数倍なので、計算量的には無視できる。
というわけで、上の式を整理すれば、計算量が出てくるのだ。
まず、括弧を外そう。
$$\frac{n(n – 1)}{2} = \frac{n^2}{2} – \frac{n}{2}$$
計算量の足し算(引き算)では、大きい方を残すので…
$$O(\frac{n^2}{2} – \frac{n}{2}) = O(\frac{n^2}{2})$$
最後、計算量では定数倍を無視するので…
$$O(\frac{n^2}{2}) = O(n^2)$$
つまり、バブルソートの計算量は\(O(n^2)\)となる。
バブルソートのサンプルプログラム
最後に、参考までにバブルソートを行うプログラムを載せておこう。
言語はJavaScriptで書いているが、他の言語でもほぼ同じ形で書けるはずだ。
function bubble_sort(arr){
var n = arr.length;
for(var i = 0; i < n - 1; i++){
for(var j = n - 1; j > i; j--){
if(arr[j - 1] > arr[j]){
var tmp = arr[j];
arr[j] = arr[j - 1];
arr[j - 1] = tmp;
}
}
}
}
おわりに
今回は、バブルソートを解説した。
単純なソートアルゴリズムの中でも特に簡単なものなので、動きをしっかり把握しておこう。
今後、同じように選択ソート、挿入ソートも解説しようと思う。
コメント