クイックソートについての基本的質問

このQ&Aのポイント
  • Cのアルゴリズムを勉強中の者です。某プログラム本に以下のソースが載っていたのですが、分からない点が1点あります。
  • QAscendingSort関数の仮引数first、lastが何を意味しているか分かりません。
  • 取り敢えず自分ではfirstは配列aの最前列要素の添え字、lastは配列aの要素数と考えたのですが…。
回答を見る
  • ベストアンサー

クイックソートについての基本的質問

Cのアルゴリズムを勉強中の者です。 某プログラム本に以下のソースが載っていたのですが 分からない点が1点あります。 とりあえずソースは以下の通りです。 /***********(プログラム開始)************/ /************************************* * 昇順のクイックソート * *************************************/ void QAscendingSort(char a[], int first, int last) { char temp; int forward = first; int backward = last - 1; /* a[last]は枢軸に充てる */ register char pivot = a[backward--]; /* 配列の要素が1個なら大小のグループ分けを終了する */ if (first >= last - 1) return; /* 配列の要素を枢軸を基準にして、左側に小のグループ、 * 右側に大のグループと振り分ける */ while (1) { /* 枢軸より小ならforwardを前進(インクリメント)する */ while (a[forward] < pivot) forward++; /* 枢軸より大ならbackwardを後退(デクリメント)する */ while (a[backward] > pivot) backward--; /* forwardとbackwardが交差すれば配置換えを終了 */ if (forward >= backward) break; /* forward要素とbackward要素を交換する */ temp = a[forward]; a[forward++] = a[backward]; a[backward--] = temp; } /* pivotを所定の位置に置く */ a[last - 1] = a[forward]; a[forward] = pivot; /* 左側のグループをさらに大小のグループに分類する */ QAscendingSort(a, first, forward); /* 右側のグループをさらに大小のグループに分類する */ QAscendingSort(a, forward + 1, last); } int main(void) { int i; /* char型の配列を用意する */ char a[] = {'O', 'W', 'E', 'R','M', 'T', 'Z', 'Q', 'J', 'D', 'A', 'I', 'F', 'B', 'P', 'X', 'Y', 'G', 'K', 'S', 'H', 'U', 'C', 'V', 'N', 'L'}; int n = sizeof(a) / sizeof(char); puts("<昇順にクイック\ソ\ー\ト>"); /* 配列の内容を表示 */ puts("[\ソ\ー\ト前]"); for (i = 0; i < n; i++) printf("%c ", a[i]); printf("\n"); /* 配列を昇順に分類する */ QAscendingSort(a, 0, n); /* 配列の内容を表示 */ puts("[\ソ\ー\ト後]"); for (i = 0; i < n; i++) printf("%c ", a[i]); printf("\n"); return 0; } /***********(プログラムはここまで)************/ 分からないのはQAscendingSort関数の 仮引数first、lastが何を意味しているかです。 取り敢えず自分ではfirstは配列aの最前列要素の添え字、 lastは配列aの要素数と考えたのですが…。 以上、長々と書いてしまいましたが、 よろしくお願い致します。

質問者が選んだベストアンサー

  • ベストアンサー
  • chie65535
  • ベストアンサー率43% (8521/19370)
回答No.2

>取り敢えず自分ではfirstは配列aの最前列要素の添え字、 >lastは配列aの要素数と考えたのですが…。 違います。 first、lastは、文字通り「最初の要素番号」と「最後の要素番号+1」を指定します。 >/* 左側のグループをさらに大小のグループに分類する */ >QAscendingSort(a, first, forward); >/* 右側のグループをさらに大小のグループに分類する */ >QAscendingSort(a, forward + 1, last); の部分は「forwardを境に、左グループと右グループを処理」してます。 最初に与えられたlast(これは「int n = sizeof(a) / sizeof(char);」の式により「最後の要素番号+1」になる)は、最後の最後まで変化しません。 もしlastが「最後の要素番号+1」ではなく「要素数」であるなら「先頭の要素番号を変えたなら、要素数も変えなければならない筈」です。 引数は、以下のような値が渡される筈です。 ・最初の呼び出し:a,0,26 ・forwardが仮に10になった場合の左グループの呼び出し:a,0,10 ・forwardが仮に10になった場合の右グループの呼び出し:a,11,26 「lastが要素数」であるなら、右グループの引数は「a,11,26」ではなく「a,11,15」になる筈です。11番目から26番目までの要素数は15個ですからね。 でも、そうは動きません。 最初のlastを求める式「int n = sizeof(a) / sizeof(char)」が、「配列の要素数を求める式」として良く書く式「sizeof(配列名) / sizeof(1要素の型)」と同じ形をした式だったので「lastは要素数」だと勘違いしたのでしょう。 ですが、本当は「lastは最後の要素位置+1」です。

kerberosMA
質問者

お礼

有難うございました。 自分でも分からなかったところがすっきりしました。 返事が遅れて申し訳ありませんでした。

その他の回答 (1)

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.1

「last」という英単語は何を意味すると思いますか? 厳密にはちょっと違うんだけど....

kerberosMA
質問者

お礼

「最後」ですよね?ってことは 配列の最後の添字ってことですか?

関連するQ&A

  • クイックソートプログラムでセグメンテーション違反がでるのですが

    クイックソートのプログラムを作成したのですが、 実行するとセグメンテーション違反が発生して、上手くいきません。何処に原因があるのでしょうか? また、セグメンテーションン違反とはどういったころなのでしょうか? アドバイス宜しくお願いします。 #include <stdio.h> int quick_sort(int *a,int start,int end); int partition(int *a,int start,int end); main() { int n; int a[n]; int i; printf("ソートしたい要素の個数は?\n"); scanf("%d",&n); for(i=0;i<=n-1;i++) a[i]=0; for(i=0;i<=n-1;i++){ printf("%dのデータを入力してください。\n",i+1); scanf("%d",&a[i]); } printf("ソート前のデータは以下の通り\n"); for(i=0;i<=n-1;i++) printf("%d ",a[i]); quick_sort(*a,1,n-1); printf("ソート後のデータは以下の通り\n"); for(i=0;i<=n-1;i++) printf("%d ",a[i]); } int quick_sort(int *a,int start,int end) { int pivot; if(end-start>0){ pivot=partition(a,start,end); quick_sort(a,start,pivot-1); quick_sort(a,pivot+1,end); } } int partition(int *a,int start,int end) { int i,j,pivot,tmp; i=start-1; j=end; pivot=a[end]; while(1){ while(a[++i]<pivot); while(i<--j && a[j]>pivot); if(i>=j) break; tmp=a[i]; a[i]=a[j]; a[j]=tmp; } a[end]=a[i]; a[i]=pivot; return i; }

  • クイックソートについて

    下記のソースプログラムのquick_sort_coreの部分がわかりません。 わかる方助けてください。 あとこのソースを降順にした場合の変更点も教えていただけると助かります。 #include <stdio.h> /* printf()利用のため */ #include <stdlib.h> /* rand()利用のため */ #include <time.h> /* clock()利用のため */ #define N 10 /* 整列対象要素数 */ /** * 配列の中身を表示する関数 * @param a 表示する配列 * @param n 配列の要素数 */ void print_array(const int a[], int n) { int i; for (i = 0; i < n; i++) { printf("%d ", a[i]); } printf("\n"); } /** * 整列されているかどうか確認する関数 * @param a 確認対象配列 * @param n 配列の要素数 */ void is_sorted(const int a[], int n) { int i; for (i = 0; i < n - 1; i++) { if (a[i] > a[i + 1]) { printf("昇順に整列されていません\n"); return; } } printf("昇順に整列されています\n"); } /** * クイックソートの本体 * @param a 整列対象配列 * @param l 対象の最初の要素番号 * @param r 対象の最後の要素番号 */ void quick_sort_core(int a[], int l, int r) { /* ここを実装する */ } /** * これまでの整列関数のインターフェースにあわせるクイックソート呼び出し関数 * @param a 整列対象配列 * @param n 配列の要素数 */ void quick_sort(int a[], int n) { quick_sort_core(a, 0, n - 1); } int main(void) { int i; int n = N; /* 整列対象要素数 */ int a[N]; clock_t start,end; /* 0からRAND_MAXの一様乱数をn個生成し、配列aに格納 */ for (i = 0; i < n; i++) { a[i] = rand(); /* 出力確認(print_array)するときは a[i]=rand()%100 としておくと見やすい */ } /* 昇順にソートされているか確認(デバッグ用) */ is_sorted(a, n); /* 配列の中身を確認(デバッグ用) */ print_array(a, n); /* 開始時刻の取得 */ start = clock(); /* クイックソート関数の呼び出し */ quick_sort(a, n); /* 終了時刻の取得 */ end = clock(); /* 配列の中身を確認(デバッグ用) */ print_array(a, n); /* 昇順にソートされているか確認(デバッグ用) */ is_sorted(a, n); /* 実行時間の表示 */ printf("%d 個の要素のクイックソートの実行に %f [秒]かかりました\n", n, (double)(end - start) / CLOCKS_PER_SEC); return 0; }

  • クイックソートの比較交換回数について

    クイックソートの比較交換回数を変数countで計算し、表示させようとしているのですがうまくいきません。 改善策を教えていただけないでしょうか。 /* クイックソート */ void quickSort(randomlist_t data[],int n){ /* 実質的な処理は何もせず、クイックソートを実際に行う関数を呼び出すのみ */ int count = 0; quickSort_1(data,0,n-1,count); printf("count:%d\n",count); } /* 実際にクイックソートを行う関数 */ void quickSort_1(randomlist_t data[],int l,int r,int count){ int v; if(l >= r) /* 整列する要素が1つなら、何もしないでリターンする */ return; v = partition(data,l,r,count); /* 枢軸vを基準に分割する */ quickSort_1(data,l,v-1,count); /* 左の部分の配列a[l]~a[v-1]を整列する */ quickSort_1(data,v+1,r,count); /* 右の部分の配列a[v+1]~a[r]を整列する */ } /* 配列a[l]~a[r]を分割する。枢軸の添え字を返す */ int partition(randomlist_t data[],int l,int r,int count){ int i,j,pivot; randomlist_t t; i = l - 1; j = r; /* ポインタiとjを初期化する */ pivot = data[r].no; /* 一番右端の要素を枢軸にする */ for(;;){ /* ポインタiとjとがぶつかるまで繰り返す */ while(data[++i].no < pivot) count++; /* ポインタiを右へ進める */ while(i < --j && pivot < data[j].no) count++; /* ポインタjを左へ進める */ if(i >= j) break; /* ポインタiとjがぶつかったらループを抜ける */ t = data[i]; data[i] = data[j], data[j] = t; /* iの指す要素とjの指す要素を交換する */ count++; } t = data[i]; data[i] = data[r]; data[r] = t; /* a[i]と枢軸を交換する */ count++; return i; }

  • クイックソートのプログラムを実行するとエラーが出る

    プログラミング初心者のものです。 ソースのファイル名はquicksortという名前にして、コンパイル&実行すると、 「Quicksortが原因でQUICKSORT.EXEにエラーが発生しました。Quicksortは終了します。問題が解決しない場合は、コンピュータを再起動してください。」 と、 「Quicksortが原因でKERNEL32.DLLにエラーが発生しました。Quicksortは終了します。問題が解決しない場合は、コンピュータを再起動してください。」 と言うエラーが出てきて最後まで実行されません。 どうしてでしょうか?ちなみにソースは以下のものです。 #include<stdio.h> #define N 10 void quicksort(int a[],int first,int last) { int i,j,p,w,k; k=(first+last)/2; p=a[k]; i=first; j=last; while(i<=j){ while(a[i]<p){ i++; } while(a[j]>p){ j--; } w=a[i]; a[i]=a[j]; a[j]=w; i++; j--; } if(first>j){ quicksort(a,first,j); } if(last>i){ quicksort(a,i,last); } } main() { int a[]={18,97,59,72,5,38,11,43,67,26}; int i; printf("Before:"); for(i=0;i<N;i++){ printf("%4d",a[i]); } printf("\n"); quicksort(a,0,N-1); printf("After:"); for(i=0;i<N;i++){ printf("%4d",a[i]); } printf("\n"); return(0); }

  • クイックソートについて

    クイックソートのプログラムなんですが、 セグメンテーション違反で実行出来ません。 どこがおかしいのでしょうか? int main(void) { FILE *fp; int a[10],b=0,n; clock_t start; double jikan; if( (fp = fopen ("quicksort.txt","r") ) == NULL ) { printf("ファイルが見つかりません : quicksort.txt\n"); exit(1); } while( fscanf(fp, "%d", &a[b]) != EOF ) { b++; } start = clock(); quick_sort(a,n); jikan = clock() - start; for(n = 0; n < b ; ++n) { printf("%d ",a[n]); } printf("計算時間 %.3f 秒 \n", jikan/CLOCKS_PER_SEC); return 0; } int partition(int a[], int l, int r) { int i,j,pivot,t; i = l-1; j = r; pivot = a[r]; for(;;) { while( a[++i] < pivot ) ; while( i < --j && pivot < a[j] ) ; if( i >= j ) break; t=a[i]; a[i]=a[j]; a[j]=t; } t=a[i]; a[i]=a[r];a[r]=t; return i; } void quick_sort_1(int a[],int l,int r) { int v; if( l >= r ) return; v = partition( a, l, r); if( l < v-1 ) quick_sort_1( a, l, v-1); if( v+1 < r ) quick_sort_1( a, v+1, r); } void quick_sort(int a[],int n) { quick_sort_1( a, 0, n-1); }

  • クイックソート

    今クイックソートのプログラム作成をしていますが、分からない箇所が幾つかあります。下は、作成した時のプログラムリストです。 class QuickSort{ static int compare = 0;   static int copy = 0;    static int swap = 0; static void swap(int a[], int i, int j){    int tmp; tmp = a[i];copy++; a[i] = a[j];copy++; a[j] = tmp;copy++; swap++;} static void showArray(int a[], int l, int r){//配列の内容を表示するメソッドで、動作:部分配列a[l]~a[r]の要素を表示。これが分かりません。     int a[l] = {1,2,3,4,5,6,7,8,9,10}; int a[r] = {11,12,13,14,15,16,17,18,19,20};} static void initArray(int a[], int N){ int i;     a[0]=0; for(i=1;i<N;i++) a[i] = i; for(i=1;i<N;i++){ int j,k; j = (int)(java.lang.Math.random()*(N-1)) + 1; k = (int)(java.lang.Math.random()*(N-1)) + 1; swap(a,j,k);}} public static void (int a[],int l, int r) { if (r+1-l <= 1) return; int e = (l+r+1)/2; int c = 0; return a[c]; } static int partition(int a[], int l, int r, int pivot){// 配列の分割を行うメソッドです。そのあと分割に要した比較回数(=iの増加分+jの減少分)を変数compareに加算し、iの値を戻り値として返さないといけませんが、そこんとこが分かりません。 while (true) { while (a[l] < pivot) l = l + 1; while (a[r] >= pivot) r = r - 1; if (l > r) return l; swap(a, l, r); } } static void swap(int[] a, int i, int j) { int tmp = a[i]; a[i] = a[j]; a[j] = tmp; } } int i=0; int j=0; return i; } static void quicksort(int[] a, int l, int r){//(カットオフなしで)再帰的にクイックソートを行うメソッドです。paritionメソッドを用い、a[l]~a[r]を分割値a[r]で分割するのが条件ですが、ここも全然分かりません。 if(l < r){ int i = l; int j = r; int x = a[(i+j)/2]; do{ while(a[i]<x) i++; while(a[j]>x) j--; if(i<=j) { swap(a,i,j); i++; j--; } }while(i<=j); quickort(a,l,j); quicksort(a,i,r); } } static void swap(int[] a, int s, int t){ int temp = a[s]; a[s] = a[t]; a[t] = temp; return; int i,pivot; } public static void main(String args[]){ //上で作ったメソッドを用いて、ソート過程を表示しながらクイックソートを実行 //手順は次のようになる。     1.要素を(20個もつ)整数型配列aを宣言     2.整数型変数Nに配列aの要素数を保存     3.initArrayメソッドを用いて配列aを初期化 4.showArrayメソッドを用いてソート前の配列aの内容を表示 5.変数compare,copyの値を0に初期化 6.quickSortメソッドを用いて配列aを選択ソート     7.showArrayメソッドを用いてソート後の配列aの内容を表示 8.ソートにかかった比較・コピーの回数を表示 //これも分かりません。 } 分かる人がいましたら教えて下さい。お願いします。

  • クイックソート キー比較回数をカウント

    クイックソートでキー比較回数をカウントする。 以下のように書いたのですが、 22行目(C++;)の状態ではまだ何かが足りないらしいのですが、何が足りないのか教えてください。 /* ここからプログラム */ #include <stdio.h> #define N 10 /* 配列aの要素数 */ static int a[N] = { 7, 20, 1, 18, 9, 15, 24, 10, 3, 12}; int main() { __int i, j; /* ループカウンタ */ __int temp; /* 交換用 */ __int pivot; /* 基準値 */ __int C = 0; /* キー比較回数C */ __int M = 0; /* データ置換回数M */ __i = 0; /* iを先頭要素番号 */ __j = N - 1; /* jを末尾要素番号 */ __pivot = a[j / 2]; /* 任意の要素を取り出し基準値 */ __while (i <= j){ ____C++; /* 22行目,キー比較回数カウント */ ____while (a[i] < pivot) /* a[i] >= pivot が見つかるまで */ ______i++; /* iを増加 */ ____while (a[j] > pivot) /* a[j] <= pivot が見つかるまで */ ______j--; /* jを減少 */ ____if (i < j && a[i] != a[j]) { /* i < j かつ a[i]≠a[j] ならば */ ______temp = a[i]; /* a[i]とa[j]を交換 */ ______a[i] = a[j]; ______a[j] = temp; ______M++; /* データ置換回数回数カウント */ ______i++; /* iを増加 */ ______j--; /* jを減少 */ ____} ____if (i == j || a[i] == a[j]){ /* i = j かつ a[i] = a[j] ならば */ ______i++; /* iを増加 */ ______j--; /* jを減少 */ ____} __} /* ここから結果表示 */ __for (i = 0; i < N; i++) ____printf("%3d ", a[i]); __printf("\n"); __printf(" キー比較回数 : %3d回\n データ置換回数: %3d回\n", C, M); /* ここまで結果表示 */ __return 0; } /* ここまでプログラム */

  • クイックソートがうまくいかない

    クイックソートとはこういう仕様のソートのことです。 (1)バラバラの配列から基準値を設定する。 (2)前から順番に辿って行って基準値よりも大きいならば、基準値よりも後に移動、小さいならば、基準値よりも前に移動させる。(私のプログラムの場合は基準値を配列の一番目の数に設定しています。) (3)小さいほうと大きいほうの二つのグループに分かれたらまたそれらのグループごとにクイックソートにかける。 (4)二つのグループを分けることができなくなればソート完了。 自分で確かめてみたのですが、どうやら関数QuickSortの中でさらに再帰的にQuickSortにかけるときにバグが発生しているようです。ですが、なぜバグが起きるのかわかりません。 お手数ですが、よろしくお願いいたします。 #include<stdio.h> #include<stdlib.h> #include<time.h> #include<string.h> #define N 10 void QuickSort(char mi,char *hai,int youso) { char *shoubox; char *bigbox; int k=0; int q=0; int i,len,len2; /*もし配列haiの大きさが1ならば終了*/ if(strlen(hai)==1) { return; } /*配列の2番目から最後まで基準値からの大小により仕分けしていく。大きいならば、配列bigboxに小さい*/ /*ならば配列shouboxにそれぞれ入れる。*/ for(i=1;i<youso-1;i++) { if(hai[i]>mi) { bigbox[k++]=hai[i]; } else { shoubox[q++]=hai[i]; } } /*仕分け処理はここまで*/ /*小さいほうの配列と大きいほうの配列をそれぞれクイックソートにかける再帰処理。*/ QuickSort(shoubox[0],shoubox,q); QuickSort(bigbox[0],bigbox,k); /*それぞれのソートが完了したら、配列haiに「小さいほうの配列」→「基準値」→「大きいほうの配列」 の順に値を代入していく。*/ len=strlen(shoubox); for(i=0;i<len;i++) { hai[i]=shoubox[i]; } hai[len]=mi; len2=strlen(bigbox); for(i=(len+1);i<len+len2;i++) { hai[i]=bigbox[i]; } /*代入処理ここまで。*/ } int main(void) { char array[N]; char m; int i,val,j; srand(time(NULL)); /*(1~10)までの数字をランダムに入力する処理*/ for(i=0;i<N;i++) { do{ val=rand()%10; for(j=0;j<i;j++) { if(val+'0'==array[j]) { break; } } }while(j<i); array[i]=val+'0'; } /*ランダムに値を入力する処理ここまで*/ m=array[0]; /*値配列の最初を基準値mに設定*/ QuickSort(m,array,N); /*基準値、配列、要素数を実引数として、クイックソートを呼び出す。*/ /*昇順に並べ替えた配列arrayを出力する。*/ for(i=0;i<N;i++) { printf("array[%d]=%c\n",i,array[i]); } return 0; }

  • クイックソートのアルゴリズム

    http://www1.cts.ne.jp/~clab/hsample/Sort/Sort9.html こちらにあったソースを改造しました。 #include <stdio.h> void QSort(int x[ ], int left, int right) {   int i, j, temp;   int pivot;   i = left;   j = right;   pivot = x[(left + right) / 2]; // 6,5,4,3 // pivot=4   while (1) {   while (x[i] <= pivot) i++; // サイトのソースは = が無かったから付けた // 2回目のループでは j=2   while (pivot < x[j]) j--; // 2回目のループで x[-1] を参照しませんか? // 初回ループで i=0, j=3   if (i >= j) break;   temp = x[i]; x[i] = x[j]; x[j] = temp; // 交換   i++; j--;   }   ShowData(x, 10); // 途中経過を表示   if (left < i - 1) QSort(x, left, i - 1);   if (j + 1 < right) QSort(x, j + 1, right); } void ShowData(int x[ ], int n) {   int i;   for (i = 0; i < n ; i++) printf("%d ", x[i]);   printf("\n"); } void main(void) {   int x[] = {6, 3, 1, 7, 0, 4, 8, 5, 2, 9};   int n = 10;   printf("ソート前:\n");   ShowData(x, n);   printf("ソート中:\n");   QSort(x, 0, n - 1);   printf("ソート後:\n");   ShowData(x, n); } テストデータが 6,5,4,3 だと、 2回目のループで x[-1] を参照しませんか? 今コンパイラが使えません。 教えてください。 それと、=を付けたんですが、付けないといけませんよね?

  • クイックソートのピボットの値を基準に分割される配列の表示

    クイックソートのプログラミングしたのですが 何処にピボットの値を基準に分割される配列の表示 するプログラムをいれていいかもわからない だけでなく、どの様なプログラムを入れたらいいかも 解りません 作ったプログラムは #include <stdio.h> #define MAX 1000 #define FMAX 8 void quick(int a[],int left,int right); int main(void) { int kosu; int atai[MAX]; int i; char fname[FMAX+1]; FILE *fp; printf("並び替える整数の個数 : "); scanf("%d",&kosu); printf("データが格納されたファイル名:"); scanf("%s",fname); if((fp=fopen(fname,"r"))==NULL) { printf("%sファイルがfopenできませんでした。処理を終了します",fname); exit(1); } for(i=0;i<kosu;i++) { fscanf(fp,"%d",&atai[i]); } quick(atai,0,kosu-1); puts("昇順に並び替えました"); for(i=0;i<kosu;i++) { printf("%d番目の整数 : %d\n",i+1,atai[i]); } fclose(fp); return 0; } void quick(int a[],int left,int right) { int pl=left; int pr=right; int pivot; int tmp; int i; pivot=a[(pl+pr)/2]; printf("ピボットの値は%d\n",pivot); do{ while(a[pl]<pivot) { pl++; } printf("\n"); while(a[pr]>pivot) { pr--; } if(pl<=pr) { { tmp=a[pl]; a[pl]=a[pr]; a[pr]=tmp; } pl++; pr--; } }while(pl<=pr); if(left<pr) { quick(a,left,pr); } if(pl<right) { quick(a,pl,right); } } です 解るかた詳しく教えてください お願いします

専門家に質問してみよう