クイックソートを説明する際、
しかし、実際に書く再帰を用いたプログラムでは、 分割はこういうふうには進まない。 そのことは、 《ソートの過程を「色+数字」で表示する》、 《ソートの過程を色で表示する》、 《ソートの過程を色で表示する(table 版)》 を見てもわかる。
上のように“見せる”ためには、再帰をやめ、次のようにすればよいのではないか。 クイックソートの各段階では pivot と呼ばれる要素を選ぶが、 その段階の終了時にその要素が収まる位置は、 最後にその要素が収まるべき位置であることに注意する。 ソートすべき配列と同じ大きさの char の配列をとり、 すべてに「未確定」を示す値を入れておく。 クイックソートの本質的部分となる関数からは、最後の再帰呼び出しを除いておく。 最初に配列全体に対しその関数を適用したら、pivot の収まった位置は「確定」とする。 次は、「配列の最初からいま確定した要素の手前まで」と 「いま確定した要素の次から配列の最後まで」にその関数を適用し、 pivot の収まった位置 --- 二箇所ある --- を「確定」とする。 次は三箇所の「確定」で区切られた四つの部分にその関数を適用する。---
(pivot の収まる位置によっては適用される区間が 2 のベキ乗個に分かれないこともある。)
プログラムは以下の通り。
#include <stdio.h> #include <stdlib.h> /* srand() */ #include <time.h> /* time() */ #define COLOR #ifdef COLOR #define N 100 #else #define N 26 #endif void init(void); void print(void); #ifdef COLOR void printincolor(int i); #endif void quicksort(int i, int j); void swap(int i, int j); int find(int i, const int k); int a[N]; /* ソートされる配列 */ char c[N]; /* 確定済みかどうかのフラグ。0 は未確定、1 は確定 */ int main() { int i, j; #ifdef COLOR printf("<html>\n"); /* html の最初の部分。略式 :-) */ printf("<body>\n"); printf("<p>\n"); printf("<table border=\"1\" cellspacing=\"0\">\n"); #endif init(); print(); while ((i = find(0, 0)) != N) { /* 未確定がある限り */ do { j = find(i, 1) - 1; /* その未確定ブロックの右端を探して */ quicksort(i, j); /* その間を“クイックソート” */ } while ((i = find(j + 1, 0)) != N); /* 次を探し、見つかる限り */ print(); } #ifdef COLOR printf("</table>\n"); printf("</p>\n"); /* html の最後の部分。略式 :-) */ printf("</body>\n"); printf("</html>\n"); #endif return 0; } void init(void) { int i; for (i = 0; i < N; i++) { a[i] = i; c[i] = 0; } srand((unsigned)time(NULL)); /* 現在時刻で乱数の種を初期化 */ for (i = N-1; i > 0; i--) { /* シャッフルする */ swap(i, rand() % (i+1)); } return; } #ifndef COLOR void print(void) { int i; for (i = 0; i < N; i++) { printf(" %02d", a[i]); } printf("\n"); for (i = 0; i < N; i++) { printf(" %c", c[i] ? '|' : ' '); } printf("\n"); } #else void print(void) { int i; printf("<tr>\n"); for (i = 0; i < N; i++) { printincolor(i); } printf("</tr>\n"); printf("<tr>\n"); for (i = 0; i < N; i++) { if (c[i] == 0) { printf("<td bgcolor=\"black\"> </td>\n"); } else { printincolor(i); } } printf("</tr>\n"); } /* a[i] を色で表示。<td bgcolor="#0fffff"> </td> のように。*/ void printincolor(int i) { printf("<td bgcolor=\""); switch(a[i] / 20) { case 0: printf("#ff%02x0f", 15 + a[i] % 20 * 12); break; case 1: printf("#%02xff0f", 255 - a[i] % 20 * 12); break; case 2: printf("#0fff%02x", 15 + a[i] % 20 * 12); break; case 3: printf("#0f%02xff", 255 - a[i] % 20 * 12); break; case 4: printf("#%02x0fff", 15 + a[i] % 20 * 12); break; default: ; } printf("\"> </td>\n"); } #endif void swap(int i, int j) { int tmp; tmp = a[i]; a[i] = a[j]; a[j] = tmp; } /* ↓あまりしっかりチェックしていないのでミスがあるかも */ void quicksort(int left, int right) { int i, j; int pivot; if (left == right) { c[left] = 1; return; } pivot = a[left]; i = left; j = right; while (i < j) { while (i <= right && a[i] <= pivot) { i++; } while (j >= left && a[j] >= pivot) { j--; } if (i < j) { swap(i, j); i++; j--; } } /* ここへきたときは i == j または i == j + 1 になっている */ if (i != j || a[i] > pivot) { i--; } swap(left, i); c[i] = 1; } /* i 以上で最初に c[i] == k となる i を返す。見つからなければ N を返す */ int find(int i, const int k) { while (i < N && c[i] != k) { i++; } return i; }
「#define COLOR」を消すと次のような出力が得られる。 下に「|」が書かれた要素は確定している。
16 19 21 15 24 08 17 18 10 20 12 13 25 23 01 00 11 02 09 06 22 14 03 04 07 05 11 05 07 15 04 08 03 14 10 06 12 13 09 02 01 00 16 23 25 20 22 18 17 24 21 19 | 09 05 07 00 04 08 03 01 10 06 02 11 13 12 14 15 16 21 19 20 22 18 17 23 24 25 | | | 06 05 07 00 04 08 03 01 02 09 10 11 12 13 14 15 16 18 19 20 17 21 22 23 24 25 | | | | | | | 03 05 02 00 04 01 06 08 07 09 10 11 12 13 14 15 16 17 18 20 19 21 22 23 24 25 | | | | | | | | | | | | | | 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | | | | | | | | | | | | | | | | | | | 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | | | | | | | | | | | | | | | | | | | | | | | 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | | | | | | | | | | | | | | | | | | | | | | | | | 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | | | | | | | | | | | | | | | | | | | | | | | | | |
上のままだと以下のような出力になる。 奇数行目は配列の値を対応する色で示したもの。 偶数行目には、未確定は黒で、確定はその上のマスと同じ色で表示されている。 (色の対応については 《ソートの過程を色で表示する》 を参照。)
2006-03-27 (1) 02:04:39 +0900