質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
アルゴリズム

アルゴリズムとは、定められた目的を達成するために、プログラムの理論的な動作を定義するものです。

Q&A

解決済

2回答

322閲覧

クイックソートの処理内容

m.g2017

総合スコア19

アルゴリズム

アルゴリズムとは、定められた目的を達成するために、プログラムの理論的な動作を定義するものです。

0グッド

0クリップ

投稿2017/09/03 12:01

『プログラミングの宝箱 アルゴリズムとデータ構造 第2版』(SBクリエイティブ株式会社)の内容の
第1章、クイックソートのjava版サンプルの以下の処理内容がわかりません。
文法的にわからない箇所はないのですが、なぜこの処理で並べ替えができるのか?
どの箇所でどういう作業をしているのかがわかりません。
quickSort(int bottom, int top, int[] data)メソッドの処理内容について
具体的に何をやっているのか理解できる方がいれば、解説お願いいたします。

import java.util.Random;

class List1_3 {
// データの件数
private static final int N = 10;
private static int[] sort = new int[N];

private static void quickSort(int bottom, int top, int[] data) { int lower,upper; if (bottom >= top) return; // 先頭の値を「適当な値」とする int div = data[bottom]; for (lower = bottom, upper = top; lower < upper;) { while (lower <= upper && data[lower] <= div) lower++; while (lower <= upper && data[upper] > div) upper--; if (lower < upper) { int temp = data[lower]; data[lower] = data[upper]; data[upper] = temp; } } // 最初に選択した値を中央に移動する int temp = data[bottom]; data[bottom] = data[upper]; data[upper] = temp; quickSort(bottom, upper - 1, data); quickSort(upper + 1, top, data); } public static void main(String args[]) { Random random = new Random(); System.out.println("ソート準備"); for (int i = 0; i < N; ++i) { sort[i] = random.nextInt(1000); System.out.print(sort[i] + " "); } System.out.println("\nソート開始"); quickSort(0, N - 1, sort); System.out.println("ソート終了"); for (int i = 0; i < N; ++i) { System.out.print(sort[i] + " "); } }

}

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

クイックソートの基本的なアルゴリズムは swordone さんのおっしゃるとおりです。このコードでは、「ある区間」のquickSort をまかされたら、「基準値以下をあつめた区間」「基準値」「基準値より大きい区間」の三つに (この順に) わけ、「基準値以下をあつめた区間」と「基準値より大きい区間」のそれぞれに再帰的に quickSort をさせて、最終的に全体がソートされる、ということです。ですので、私は『「基準値以下をあつめた区間」「基準値」「基準値より大きい区間」の三つに (この順に) わける』ことがどうやって実現されているのか、図解します。
イメージ説明

最初 (1) は、関数 quickSort() が呼ばれたところです。長いデータ列の中の bottom (以下 b) と top (同じく t) の間の区間のソートを担当することになります。lower (l) と upper (u) は、それぞれ最初は bt の位置から始まり、互いに近づく方にひとつずつ移動していきます。この移動を行っているのがコード中の while のところですね。

(2) は、while を抜けて ul が止まったところを表しています。背景色が灰色のところは基準値 div との大小関係がわからないところ (最初は全部) で、この時点でわかったところが色づけしてあります。div 以下のところが黄色、div より大きいところが青になっています。while が止まった、ということで、とまったところの l のころは青、u のところが黄色になっているにご注目ください。
で、

java

1int temp = data[lower]; 2data[lower] = data[upper]; 3data[upper] = temp;

のくだりで lu のところの値を交換します。

交換が終わったところが (3) です。

で、まだ lu より小さいので、また lu を近づける作業をしていきます。(4), (5)。

lu がぶつかったら、最初の for を抜けます。これが (6) のところです。
divl のところの値を交換しろ、とのことなので交換したのが (7) です。これで、『「基準値以下をあつめた区間」「基準値」「基準値より大きい区間」の三つに (この順に) わける』ことができました。
できたら、「基準値以下をあつめた区間」「基準値より大きい区間」のそれぞれを quickSort させます。

こんなんでご理解いただけるでしょうか。

投稿2017/09/03 13:13

編集2017/09/03 13:18
unau

総合スコア2468

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

m.g2017

2017/09/04 14:12

わざわざ図解での解説ありがとうございます。 なんとなく行ごとにどういう処理をしているのか見えてきました。
guest

0

はっきり言ってこのコード、無駄な処理が入っていたり意図がわからないことをしていたり、
おかしなコードになっています。
クイックソートの考え方はちょっと検索すれば山ほど出てきます。
読むのが辛ければ動画になっているものもあったりします。→クイックソート(YouTube)

ざっくり言えば、ある値より小さい数を左に、大きい数を右に寄せて2分割を繰り返していくソート法です。
"ある値"は、配列内の要素であればなんでもかまいません。これを仮に基準値と呼ぶことにします。
左から基準値よりも大きな数を探し(この場所がlower)、右から基準値よりも小さな数を探します(この場所がupper)。
この場所が問題で、左に小さな数、右に大きな数を寄せたいのに、左に大きな数があるのは困ります。
もしそうなっていた場合(lowerがupperより小さいとき)、その二つを入れ替えて正しく分けられるようにします。
これを続けていくと、lowerがupperを上回ります。この時、lowerより小さいインデックスでは基準値より小さく、upperより大きいインデックスでは基準値より大きくなるようになっているはずなので、lowerとupperが交差した場所で2分割できます。
そして、2分割したそれぞれの区画で同じことを行います。これを繰り返していけば、最終的に昇順に並び替えができることになります。

ただ、冒頭でも言った通り質問のコードは意図が読めません。特に、

java

1// 最初に選択した値を中央に移動する 2int temp = data[bottom]; 3data[bottom] = data[upper]; 4data[upper] = temp;

これがなぜ入っているかがわかりません。

追記
やっと理解できました。
もともと先頭を基準値に設定していたので、ここに入る前に
[b, a1, a2, a3, c1, c2, c3, c4, c5, c6] (b:基準値、an < b < cn)
のような状態になります(もちろん個数などは状況によって異なる)。
で、このコードの実行によって
[a3, a1, a2, b, c1, c2, c3, c4, c5, c6]
のようにして、[a3, a1, a2]の組、[c1, c2, c3, c4, c5, c6]の組のように、
1個除外してソートしようとしています。
これがないと再帰処理が余計に乗っかることになるのでそれを避ける手立てなのですね。

投稿2017/09/03 12:26

編集2017/09/03 12:42
swordone

総合スコア20651

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2017/09/03 12:44

> これがなぜ入っているかがわかりません。コメント通りの動作もできません。 コメント内の"中央"は、そのときの位置 upper であると推測できます。 そうすると、最初に決めた適当な値 div の位置を upper (中央) に確定させ、 その左右で再帰的に quickSort を呼び出す、という風に読めます。 これはまさにクイックソートの再帰そのものだと思います。
swordone

2017/09/03 12:47

これ無しで考えてみたら、2回目で下グループで先頭が下グループ末尾に移動して1つだけ分離するという、明らかな非効率であることがわかりました。
退会済みユーザー

退会済みユーザー

2017/09/03 12:57

> swordone さん 返信ありがとうございます。 追記と私のコメントが入れ違いで更新されていたようで、失礼しました。 > m.g2017 さん クイックソートはピボットの選択方法や分割に至るまでの 値交換の方法にいくらかの種類があるようなので、 実装によっては理解しにくいと感じるものがあるかもしれません。 swordone さんの書かれているように、 アルゴリズムの原理を理解した上でコードを読めば、 それが多少わかりにくいと感じても読むことができると思います。 まずはアルゴリズムの原理を正しく理解することが大切です。
m.g2017

2017/09/04 14:15

詳細な解説ありがとうございました。 unauさんの図解と合わせることで、行ごとの処理をイメージできるようになりました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問