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

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

新規登録して質問してみよう
ただいま回答率
85.48%
Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Q&A

解決済

3回答

2661閲覧

2分探索と線形探索

schooldog

総合スコア12

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

0グッド

1クリップ

投稿2016/11/07 16:05

編集2016/11/07 21:46

public class Member {
public int id;
public String name;

public Member(int id, String name) { this.id = id; this.name = name; }

public static Member findMemberById(int id, Member[] data) {

} public static Member findMemberByName(String name, Member[] data){ }

}

Member [] dataはメインクラスで定義して、会員番号順にデータを格納しました。ここから会員番号をキーにして 2 分探索 で会員を探し出すメソッド「fMI」と、会員名をキーにして線形探索で会員を探し出すメソッドを作成したいのですが、やり方がよくわかりません。後発見できなかった時はnullを返すようにしたいです。

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

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

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

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

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

guest

回答3

0

ベストアンサー

二分探索を行うユーティリティーメソッドがArraysクラスにあるのですが、おしいことにインターフェースがちょっとばかり柔軟さを欠いているために本件の検索に自然には使えません。少し工夫すると使えなくはないのですがあまりほめられた実装ではないと思うので、自前で二分探索を実装したほうが好ましいと思います。

二分探索はyubaさんの回答がアルゴリズムとしては美しいと思いますが、末尾再帰になることから(それを人間が置き換えたとしてもそれほどわかりにくくはないので)単純ループで実装してもよいと思います。コンパイラーが末尾再帰を最適化してくれると分かっている場合はあえて再帰のままにしておくのがよいかも知れませんが。(Javaコンパイラーは末尾再帰を最適化してくれるのでしたっけ・・・?)

java

1public static Member findMemberById(int id, Member[] data) { 2 int minIndex = 0; 3 int maxIndex = data.length; 4 while (minIndex < maxIndex) { 5 int index = (minIndex + maxIndex) / 2; 6 Member m = data[index]; 7 if (id < m.id) { 8 max = index; 9 } else if (id > m.id) { 10 min = index + 1; 11 } else { 12 return m; 13 } 14 } 15 return null; 16}

なお、名前での探索は線形探索とのことですが、性能ネックになる可能性があるならHashMapを検討というのがよくあるパターンですのそれも覚えておくとよいと思います。

投稿2016/11/07 17:02

KSwordOfHaste

総合スコア18394

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

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

yuba

2016/11/07 23:30

二分探索ならTreeMapもありましたね。そっちの発想をするのを忘れていました。 javaは末尾再帰の最適化してくれないです。
swordone

2016/11/08 02:05 編集

間違えた。問題なかった。
KSwordOfHaste

2016/11/08 02:19

二分探索は初歩的なアルゴリズムではありますが境界条件のプログラミングによい教材だとは思います。無限ループになるかどうか、なるとしたらどう対処するか閲覧者の方々それぞれで考えてみていただきたいと思います。プログラミングに慣れている方にとっても(少なくとも)頭の体操になりますし、初学者の方々にもアルゴリズムを考える練習になると思います。 ちなみにminIndex<maxIndexの条件があるのですが無限ループになりますか?
guest

0

本当なら丸投げ案件なので書きたくなかったんですが…

java

1private static Member findMemberById(int id, Member[]data, int left, int right) { 2 // 左右が逆になってしまったら、見つからなかった 3 if (left > right) return null; 4 5 // 中央 こう書かないと、(あまりないだろうが)オーバーフローが発生してしまうとのこと。 6 int pivot = left + (right - left) / 2; 7 8 if (data[pivot].id == id) return data[pivot]; 9 // 範囲中央のidが探索対象idより小さい場合、小さい側(左半分)に可能性はない 10 else if (data[pivot].id < id) return findMemberById(id, data, pivot + 1, right); 11 // 範囲中央のidが探索対象idより大きい場合、大きい側(右半分)に可能性はない 12 else return findMemberById(id, data, left, pivot - 1); 13}

線形探索に関しては、配列の先頭から順番に名前を探せばいいです。
ってかこれくらいググればすぐ出て来る。

投稿2016/11/07 16:46

編集2016/11/07 16:53
swordone

総合スコア20651

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

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

0

java

1private static Member findMemberById(int id, Member[]data, int left, int right) 2{ 3 // 略 4}

というちょっと引数の余計にあるメソッドを作るところから始めます。
left, rightとは何か。
data配列のどこからどこまでを探すかという範囲です。left以上right未満の範囲で調べるということ。
すると、最初のfindMemberByIdの中身はこうなります。

java

1public static Member findMemberById(int id, Member[] data) { 2 return findMemberById(id, data, 0, data.length); 3}

そしたら、最初に書いた引数の多いやつの中身をどうするか。
二分探索だから、自分自身を利用します。

java

1private static Member findMemberById(int id, Member[]data, int left, int right) 2{ 3 // 幅がゼロなら、見つからなかった 4 if (left == right) return null; 5 6 // 中央 7 int pivot = (left + right) / 2; 8 9 if (data[pivot].id == id) return data[pivot]; 10 if (data[pivot].id > id) return findMemberById(id, data, pivot + 1, right); 11 return findMemberById(id, data, left, pivot); 12}

これが二分探索です。

投稿2016/11/07 16:29

編集2016/11/07 16:41
yuba

総合スコア5568

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

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

swordone

2016/11/07 16:34

これだと、leftとrightの幅が1になった際に抜けられなくなり、オーバーフローします(pivotが以降leftの値から変われなくなるため)。 該当しないことがわかったpivotが範囲に入らないようにしなければいけません。
yuba

2016/11/07 16:36

ぎゃ。
yuba

2016/11/07 16:39

終端条件を追加しました。
yuba

2016/11/07 16:42

いや、終端条件を追加しなくても、右側の再帰を findMemberById(id, data, pivot + 1, right); と記述すればクリアできますか。
swordone

2016/11/07 16:48 編集

というか、それが本来の二分探索ですから。 そもそも不一致確定のpivotの位置まで範囲に入れる必要がないのです。
swordone

2016/11/07 16:49

しかも自分の回答書いてて気づいたのですが、大小関係の判定逆です。
yuba

2016/11/07 23:31

うわ、ほんとだ逆…
A-pZ

2016/11/08 15:15

ありがちです (・ω|
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問