web-dev-qa-db-ja.com

K&Rのqsortはどのように機能しますか?

K&RのANSI Cブックの再帰セクションでは、

クイックソートのバージョンは、可能な限り最速ではありませんが、最も簡単なバージョンの1つです。

--Cプログラミング言語(ANSI C)-pg。 87

全体として:

_/*qsort: sort v[left]...v[right] into increasing order*/
void qsort(int v[], int left, int right){

  int i, last;

  /*do nothing if array has less than 2 elements*/      
  if(left >= right)
    return;

  swap(v, left, (left + right) / 2); 
  last = left;

  /*partition*/
  for(i = left + 1; i <= right; i++)
    if(v[i] < v[left])
      swap(v, ++last, i);

  swap(v, left, last);  /*reset partition element*/
  qsort(v, left, last - 1);
  qsort(v, last + 1, right);
}
_

ここで、swap(v, i, j)は、配列vの2つのメンバーを交換します。

これがどのように配列をソートするのかを理解しようとしています。 lastは、配列を2つの小さな配列に分割し、左の境界要素と交換する配列要素のようです。次に、それは適切な境界までのすべての要素と比較され、各小さな要素が_++last_に交換されます(なぜですか?)。

最後に、パーティション要素がスワップインされ、2つのサブ配列が個別にソートされます。


再帰を正しく理解している場合、このアルゴリズムは、1つのパスの終わりに、lastの左側にあるすべての要素がlastの右側にあるすべての要素よりも小さいことを意味します。ほとんどのqsortアルゴリズムはこれよりも複雑であるため、説明を見つけるのに苦労しています。

5
user1717828

ここでのアルゴリズムは次のとおりです。

  • 配列の中央の値を取得し、(スワップによって)前面に移動します。この値がピボットです。
  • 配列の残りの部分をループします。ピボットよりも小さい値が表示されるたびに、配列の前面に近づくように交換します。具体的には、最後の小さい値がどこにスワップされたかを追跡するインデックス(lastという名前)があります。小さい値が見つかるたびに、その値をインクリメントして、その値を置く場所を見つけます。
  • 最後に到達すると、配列は以下で構成されます:
    • ピボット
    • ピボットよりも小さいすべての値
    • ピボット以上のすべての値
  • ピボットを最後に見つかった小さい方の値の位置と交換します。

この時点で、ピボットのどちらかの側にサブ配列があることがわかります。左側にはピボットより小さい値があり、右側にはピボット以上のすべての値があります。つまり、ピボットは、ソートされた最終的な配列の正確な場所にあることがわかります。また、どちらのサブ配列の値も、反対側の値と交換する必要がないことも知っています。だから今できる:

  • 同じアルゴリズムで左側のサブ配列を並べ替える
  • 同じアルゴリズムで正しいサブ配列をソートします。

再帰するたびに、サブ配列が小さくなるため、次のレベルの再帰が速くなります。また、毎回、1つの要素を正しい位置に移動してから、さらに深く進みます。最終的には、サブ配列に要素が0または1つしかないポイントに到達します。これはもちろん、サブ配列がすでにソートされていることを意味します。任意のレベルで最終的に得られるのは、右側に小さい値の並べ替えられたサブ配列があり、右側に大きい値または等しい値の並べ替えられたサブ配列がある右側のスポットの要素です。そのレベルのサブ)配列がソートされます。

9
Kevin Cathcart