すのものの「いろいろ」(その149)

クイックソート>各段階で要素が二分割されてゆく様子を見せるプログラム

クイックソートを説明する際、
 
   
       
         
のような図を書くことがある。 全体が二つに分かれ、それぞれがまた二つに分かれ、という図である。

しかし、実際に書く再帰を用いたプログラムでは、 分割はこういうふうには進まない。 そのことは、 《ソートの過程を「色+数字」で表示する》、 《ソートの過程を色で表示する》、 《ソートの過程を色で表示する(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\">&nbsp;</td>\n");
        } else {
            printincolor(i);
        }
    }
    printf("</tr>\n");
}


/* a[i] を色で表示。<td bgcolor="#0fffff">&nbsp;</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("\">&nbsp;</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


すのもの Sunomono