126. クイックソート
では疑似言語です。
入力 : 整数列 A1, A2, … An
出力 : 入力列の昇順
• 0 与えられたデータが1つor全部一緒なら終了;
• 1 入力列の中からピボットaを選択する;
• 2 for i ← 1 to n do
• 3 | if Ai <= a then
• 4 | | Ai を列P1に加える;
• 5 | end
• 6 | else
• 7 | | Aiを列P2に加える;
• 8 | end
• 9 end
• 10 P1とP2をそれぞれ整列させる;
• 11 P1の末尾にP2を連結して解とする;
127. クイックソート
まず注目していきたいのが、こちらの10
P1とP2はそれぞれクイックソートします。
これが分割統治法でよくある再帰ですね。
入力 : 整数列 A1, A2, … An
出力 : 入力列の昇順
• 0 与えられたデータが1つor全部一緒なら終了;
• 1 入力列の中からピボットaを選択する;
• 2 for i ← 1 to n do
• 3 | if Ai <= a then
• 4 | | Ai を列P1に加える;
• 5 | end
• 6 | else
• 7 | | Aiを列P2に加える;
• 8 | end
• 9 end
• 10 P1とP2をそれぞれ整列させる;
• 11 P1の末尾にP2を連結して解とする;
128. クイックソート
つまり、この関数は内部で自分を呼び出す
わけです。
入力 : 整数列 A1, A2, … An
出力 : 入力列の昇順
• 0 与えられたデータが1つor全部一緒なら終了;
• 1 入力列の中からピボットaを選択する;
• 2 for i ← 1 to n do
• 3 | if Ai <= a then
• 4 | | Ai を列P1に加える;
• 5 | end
• 6 | else
• 7 | | Aiを列P2に加える;
• 8 | end
• 9 end
• 10 P1とP2をそれぞれ整列させる;
• 11 P1の末尾にP2を連結して解とする;
129. クイックソート
最終的に細部で整列されて、
最終的に解になる!
実にいい仕組みです!
入力 : 整数列 A1, A2, … An
出力 : 入力列の昇順
• 0 与えられたデータが1つor全部一緒なら終了;
• 1 入力列の中からピボットaを選択する;
• 2 for i ← 1 to n do
• 3 | if Ai <= a then
• 4 | | Ai を列P1に加える;
• 5 | end
• 6 | else
• 7 | | Aiを列P2に加える;
• 8 | end
• 9 end
• 10 P1とP2をそれぞれ整列させる;
• 11 P1の末尾にP2を連結して解とする;
130. クイックソート
再帰になると若干人が変わります…
ごめんなさい…
入力 : 整数列 A1, A2, … An
出力 : 入力列の昇順
• 0 与えられたデータが1つor全部一緒なら終了;
• 1 入力列の中からピボットaを選択する;
• 2 for i ← 1 to n do
• 3 | if Ai <= a then
• 4 | | Ai を列P1に加える;
• 5 | end
• 6 | else
• 7 | | Aiを列P2に加える;
• 8 | end
• 9 end
• 10 P1とP2をそれぞれ整列させる;
• 11 P1の末尾にP2を連結して解とする;
131. クイックソート
ではまず0を見てみましょう。
これは再帰の終了条件となります。
最終的な処理を止めるのがこの条件です。
入力 : 整数列 A1, A2, … An
出力 : 入力列の昇順
• 0 与えられたデータが1つor全部一緒なら終了;
• 1 入力列の中からピボットaを選択する;
• 2 for i ← 1 to n do
• 3 | if Ai <= a then
• 4 | | Ai を列P1に加える;
• 5 | end
• 6 | else
• 7 | | Aiを列P2に加える;
• 8 | end
• 9 end
• 10 P1とP2をそれぞれ整列させる;
• 11 P1の末尾にP2を連結して解とする;
132. クイックソート
データが1個、ないしすべて同じ値…
つまりすでに整列されている
ということになります。
入力 : 整数列 A1, A2, … An
出力 : 入力列の昇順
• 0 与えられたデータが1つor全部一緒なら終了;
• 1 入力列の中からピボットaを選択する;
• 2 for i ← 1 to n do
• 3 | if Ai <= a then
• 4 | | Ai を列P1に加える;
• 5 | end
• 6 | else
• 7 | | Aiを列P2に加える;
• 8 | end
• 9 end
• 10 P1とP2をそれぞれ整列させる;
• 11 P1の末尾にP2を連結して解とする;
133. クイックソート
次にピボットですが
これは選び方が複数あるので後述します。
入力 : 整数列 A1, A2, … An
出力 : 入力列の昇順
• 0 与えられたデータが1つor全部一緒なら終了;
• 1 入力列の中からピボットaを選択する;
• 2 for i ← 1 to n do
• 3 | if Ai <= a then
• 4 | | Ai を列P1に加える;
• 5 | end
• 6 | else
• 7 | | Aiを列P2に加える;
• 8 | end
• 9 end
• 10 P1とP2をそれぞれ整列させる;
• 11 P1の末尾にP2を連結して解とする;
134. クイックソート
分割統治法のミソとなるのが
この2つに分ける操作です。
入力 : 整数列 A1, A2, … An
出力 : 入力列の昇順
• 0 与えられたデータが1つor全部一緒なら終了;
• 1 入力列の中からピボットaを選択する;
• 2 for i ← 1 to n do
• 3 | if Ai <= a then
• 4 | | Ai を列P1に加える;
• 5 | end
• 6 | else
• 7 | | Aiを列P2に加える;
• 8 | end
• 9 end
• 10 P1とP2をそれぞれ整列させる;
• 11 P1の末尾にP2を連結して解とする;
135. クイックソート
この時点で、
P1のすべての要素<P2のすべての要素
となりますね。
入力 : 整数列 A1, A2, … An
出力 : 入力列の昇順
• 0 与えられたデータが1つor全部一緒なら終了;
• 1 入力列の中からピボットaを選択する;
• 2 for i ← 1 to n do
• 3 | if Ai <= a then
• 4 | | Ai を列P1に加える;
• 5 | end
• 6 | else
• 7 | | Aiを列P2に加える;
• 8 | end
• 9 end
• 10 P1とP2をそれぞれ整列させる;
• 11 P1の末尾にP2を連結して解とする;
136. クイックソート
つまり、あとはそれらが整列されていれば
連結させるだけでよくなるのです。
入力 : 整数列 A1, A2, … An
出力 : 入力列の昇順
• 0 与えられたデータが1つor全部一緒なら終了;
• 1 入力列の中からピボットaを選択する;
• 2 for i ← 1 to n do
• 3 | if Ai <= a then
• 4 | | Ai を列P1に加える;
• 5 | end
• 6 | else
• 7 | | Aiを列P2に加える;
• 8 | end
• 9 end
• 10 P1とP2をそれぞれ整列させる;
• 11 P1の末尾にP2を連結して解とする;
137. クイックソート
それを行うのが、この再帰の部分ですね。
入力 : 整数列 A1, A2, … An
出力 : 入力列の昇順
• 0 与えられたデータが1つor全部一緒なら終了;
• 1 入力列の中からピボットaを選択する;
• 2 for i ← 1 to n do
• 3 | if Ai <= a then
• 4 | | Ai を列P1に加える;
• 5 | end
• 6 | else
• 7 | | Aiを列P2に加える;
• 8 | end
• 9 end
• 10 P1とP2をそれぞれ整列させる;
• 11 P1の末尾にP2を連結して解とする;
138. クイックソート
C言語のソースコードです。
見づらくてごめんなさい。
void QuickSort(int * p, int n){
int i, a; int p1[100], p2[100]; int *ptr1 = p1, *ptr2 = p2;
if (n == 1) return;
a = p[0];
for (i = 1; i < n; i++){
if (a > p[i]){
a = p[i];
break;
}
else if (a < p[i]) break;
}
if (i == n) return; // breakされない→全部同じ数字
for (i = 0; i < n; i++){ // 教科書のはaの添え字 (1~n) => (0~n-1)
if (p[i] <= a) *ptr1++ = p[i]
else *ptr2++ = p[i];
// 再帰呼び出し (分割した要素のクイックソート)
QuickSort(p1, ptr1 - p1);
QuickSort(p2, ptr2 - p2);
// 連結
for (i = 0; i < ptr1 - p1; i++) *p++ = p1[i];
for (i = 0; i < ptr2 - p2; i++) *p++ = p2[i];
}
139. クイックソート
まずこれが関数名ですね。
void QuickSort(int * p, int n){
int i, a; int p1[100], p2[100]; int *ptr1 = p1, *ptr2 = p2;
if (n == 1) return;
a = p[0];
for (i = 1; i < n; i++){
if (a > p[i]){
a = p[i];
break;
}
else if (a < p[i]) break;
}
if (i == n) return; // breakされない→全部同じ数字
for (i = 0; i < n; i++){ // 教科書のはaの添え字 (1~n) => (0~n-1)
if (p[i] <= a) *ptr1++ = p[i]
else *ptr2++ = p[i];
// 再帰呼び出し (分割した要素のクイックソート)
QuickSort(p1, ptr1 - p1);
QuickSort(p2, ptr2 - p2);
// 連結
for (i = 0; i < ptr1 - p1; i++) *p++ = p1[i];
for (i = 0; i < ptr2 - p2; i++) *p++ = p2[i];
}
140. クイックソート
つまり、ここで再帰的に呼ばれます。
void QuickSort(int * p, int n){
int i, a; int p1[100], p2[100]; int *ptr1 = p1, *ptr2 = p2;
if (n == 1) return;
a = p[0];
for (i = 1; i < n; i++){
if (a > p[i]){
a = p[i];
break;
}
else if (a < p[i]) break;
}
if (i == n) return; // breakされない→全部同じ数字
for (i = 0; i < n; i++){ // 教科書のはaの添え字 (1~n) => (0~n-1)
if (p[i] <= a) *ptr1++ = p[i]
else *ptr2++ = p[i];
// 再帰呼び出し (分割した要素のクイックソート)
QuickSort(p1, ptr1 - p1);
QuickSort(p2, ptr2 - p2);
// 連結
for (i = 0; i < ptr1 - p1; i++) *p++ = p1[i];
for (i = 0; i < ptr2 - p2; i++) *p++ = p2[i];
}
141. クイックソート
次に終了条件がこちらとなります。
void QuickSort(int * p, int n){
int i, a; int p1[100], p2[100]; int *ptr1 = p1, *ptr2 = p2;
if (n == 1) return;
a = p[0];
for (i = 1; i < n; i++){
if (a > p[i]){
a = p[i];
break;
}
else if (a < p[i]) break;
}
if (i == n) return; // breakされない→全部同じ数字
for (i = 0; i < n; i++){ // 教科書のはaの添え字 (1~n) => (0~n-1)
if (p[i] <= a) *ptr1++ = p[i]
else *ptr2++ = p[i];
// 再帰呼び出し (分割した要素のクイックソート)
QuickSort(p1, ptr1 - p1);
QuickSort(p2, ptr2 - p2);
// 連結
for (i = 0; i < ptr1 - p1; i++) *p++ = p1[i];
for (i = 0; i < ptr2 - p2; i++) *p++ = p2[i];
}
142. クイックソート
まず要素数が1の場合ですね。
void QuickSort(int * p, int n){
int i, a; int p1[100], p2[100]; int *ptr1 = p1, *ptr2 = p2;
if (n == 1) return;
a = p[0];
for (i = 1; i < n; i++){
if (a > p[i]){
a = p[i];
break;
}
else if (a < p[i]) break;
}
if (i == n) return; // breakされない→全部同じ数字
for (i = 0; i < n; i++){ // 教科書のはaの添え字 (1~n) => (0~n-1)
if (p[i] <= a) *ptr1++ = p[i]
else *ptr2++ = p[i];
// 再帰呼び出し (分割した要素のクイックソート)
QuickSort(p1, ptr1 - p1);
QuickSort(p2, ptr2 - p2);
// 連結
for (i = 0; i < ptr1 - p1; i++) *p++ = p1[i];
for (i = 0; i < ptr2 - p2; i++) *p++ = p2[i];
}
143. クイックソート
次にすべての値が同じときです。
これは配列の先頭から小さい数を
見つけれなかったとき、ということです。
void QuickSort(int * p, int n){
int i, a; int p1[100], p2[100]; int *ptr1 = p1, *ptr2 = p2;
if (n == 1) return;
a = p[0];
for (i = 1; i < n; i++){
if (a > p[i]){
a = p[i];
break;
}
else if (a < p[i]) break;
}
if (i == n) return; // breakされない→全部同じ数字
for (i = 0; i < n; i++){ // 教科書のはaの添え字 (1~n) => (0~n-1)
if (p[i] <= a) *ptr1++ = p[i]
else *ptr2++ = p[i];
// 再帰呼び出し (分割した要素のクイックソート)
QuickSort(p1, ptr1 - p1);
QuickSort(p2, ptr2 - p2);
// 連結
for (i = 0; i < ptr1 - p1; i++) *p++ = p1[i];
for (i = 0; i < ptr2 - p2; i++) *p++ = p2[i];
}
144. クイックソート
ということは、ここが配列の先頭から
小さい数を見つける部分ですね。
void QuickSort(int * p, int n){
int i, a; int p1[100], p2[100]; int *ptr1 = p1, *ptr2 = p2;
if (n == 1) return;
a = p[0];
for (i = 1; i < n; i++){
if (a > p[i]){
a = p[i];
break;
}
else if (a < p[i]) break;
}
if (i == n) return; // breakされない→全部同じ数字
for (i = 0; i < n; i++){ // 教科書のはaの添え字 (1~n) => (0~n-1)
if (p[i] <= a) *ptr1++ = p[i]
else *ptr2++ = p[i];
// 再帰呼び出し (分割した要素のクイックソート)
QuickSort(p1, ptr1 - p1);
QuickSort(p2, ptr2 - p2);
// 連結
for (i = 0; i < ptr1 - p1; i++) *p++ = p1[i];
for (i = 0; i < ptr2 - p2; i++) *p++ = p2[i];
}
145. クイックソート
最後に連結ですね。
void QuickSort(int * p, int n){
int i, a; int p1[100], p2[100]; int *ptr1 = p1, *ptr2 = p2;
if (n == 1) return;
a = p[0];
for (i = 1; i < n; i++){
if (a > p[i]){
a = p[i];
break;
}
else if (a < p[i]) break;
}
if (i == n) return; // breakされない→全部同じ数字
for (i = 0; i < n; i++){ // 教科書のはaの添え字 (1~n) => (0~n-1)
if (p[i] <= a) *ptr1++ = p[i]
else *ptr2++ = p[i];
// 再帰呼び出し (分割した要素のクイックソート)
QuickSort(p1, ptr1 - p1);
QuickSort(p2, ptr2 - p2);
// 連結
for (i = 0; i < ptr1 - p1; i++) *p++ = p1[i];
for (i = 0; i < ptr2 - p2; i++) *p++ = p2[i];
}
146. クイックソート
これだけの仕組みで
クイックソートは成り立っています。
void QuickSort(int * p, int n){
int i, a; int p1[100], p2[100]; int *ptr1 = p1, *ptr2 = p2;
if (n == 1) return;
a = p[0];
for (i = 1; i < n; i++){
if (a > p[i]){
a = p[i];
break;
}
else if (a < p[i]) break;
}
if (i == n) return; // breakされない→全部同じ数字
for (i = 0; i < n; i++){ // 教科書のはaの添え字 (1~n) => (0~n-1)
if (p[i] <= a) *ptr1++ = p[i]
else *ptr2++ = p[i];
// 再帰呼び出し (分割した要素のクイックソート)
QuickSort(p1, ptr1 - p1);
QuickSort(p2, ptr2 - p2);
// 連結
for (i = 0; i < ptr1 - p1; i++) *p++ = p1[i];
for (i = 0; i < ptr2 - p2; i++) *p++ = p2[i];
}