例3:長さ n の配列の内容を逆転するにはどうすればよいだろうか?最も単純な方法は,別の配列に逆順に要素を移していき,その後で元の配列に値を戻せばよい.具体的には,次のようにすればよい.
配列 a[0..n-1]: 与えられた配列
配列 b[0..n-1]: 作業用の配列
for i=0 to n-1
b[n-i-1] = a[i];
for i=0 to n-1
a[i] = b[i];
このようにして,2n 回のデータ移動で配列の内容を逆にすることができる.しかし,この方法では長さ
n の配列を作業用に必要とする.では,作業用の配列を使わずに同じことができるだろうか?つまり,定数作業領域のアルゴリズムはあるだろうか?実は非常に簡単である.配列の最初の要素は配列の最後尾に移動し,配列の最後尾の要素は配列の先頭に来るのだから,これらを好感すればよい.2
番目の要素についても同様で,後ろから 2 番目の要素と好感すればよい.そのような好感操作を列の半分まで続ければ,全体が逆転している.具体的なプログラムは次のとおりである.
配列 a[0..n-1]: 与えられた配列
i=0; j=n-1;
while(i < j)
a[i]とa[j]を交換(swap)
i=i+1; j=j-1;
プログラムも簡単になった.データ移動についても,不必要な移動はないので,これが最適なアルゴリズムである.
この問題を少し格調してみよう.ファイルの処理などでよく現れる問題であり,応用範囲は広いだろう.今度は一つの配列の中に
2 つの異なった内容が埋め込まれているような場合である.すなわち,与えられた配列a[0..n-1]が,前半部分(具体的には,配列の
0 番目から k-1 番目までの部分)と後半部分(配列の k 番目から n-1 番目までの部分)に分かれているものとしよう.このとき,配列の要素を,後半部分の後に前半部分の要素が並ぶように,つまり前半部と後半部の順序を逆にするように並び替えたい.このとき,操作の後で,どちらの部分でも要素は元の順序を保っていなければならないとする.
作業用の配列を使うことができるなら簡単である.前半部と後半部の境界 k
は分かっているから,先に後半部を作業用配列に移し,その後ろに前半部を移し,最後に作業用配列から元の配列に戻せばよい.
実は,作業用の配列を使わなくても線形時間で同じことが可能である.そのために上に述べた配列の内容を逆転させるアルゴリズムを用いる.まず,それぞれの部分に逆転アルゴリズムを適用する.その上で配列全体に対して同じアルゴリズムを適用する.そうすると,元の前半部は内容が逆転されたものが配列の最後尾から詰まって行くということになる.元の後半部は内容が逆転されたものが配列の先頭から詰まっていくことになる.どちらも内容の逆転が
2 回だけ起こっているので,最終的には元の順序に戻っている.具体的なプログラムは次のとおりである.
配列 a[0..n-1] 与えられた配列, a[0..k-1] 前半部,a[k..n-1] 後半部
i=0; j=k-1;
while(i<j)
a[i]とa[j]を交換し,i=i+1; j=j-1
i=k; j=n-1;
while(i<j)
a[i]とa[j]を交換し,i=i+1; j=j-1
i=0; j=n-1;
while(i<j)
a[i]とa[j]を交換し,i=i+1; j=j-1