線系探索を普通にやろうと思うと、だいたいこうなるよね?
〇 検索するデータ:Key
〇 データ列の数(配列数):N
〇 戻り値:
・データが見つかった時 :データの位置(index)
・データが見つからない時 :-1 (Not Foundの戻り値としてあり得ないindexを返す)
SequentialSearch()
・ i ← 0
■ i < N and A[i] != Key
| ・ i ← i + 1
■
▲ i => N
| ・ return -1
+---------------
| ・ return i
▼
Cで書いてみるとこんな感じ
#include <stdio.h> int main(void){ int n = 5; //配列数 int A[5] = {3, 1, 2, 5, 4}; //C言語ってconst int使えるんだっけ? int i; int Key = 8; //探索する数 printf("Key = %d\n", Key); for (i = 0; i < n; i++) { printf("%d\n", A[i]); if (A[i] == Key) { printf("見つかりました\n"); return 0; } } printf("見つかりませんでした\n"); return 0; }
ちなみに、疑似言語通りの動きをする関数を作るとこんな感じ(多分配列を関数に渡す云々とかやってないんだよね?)
#include <iostream> /// <summary> /// 大きさnの配列datの中からkeyのデータを探す関数 /// </summary> /// <param name="n">配列のサイズ</param> /// <param name="dat">配列の名前</param> /// <param name="key">探すデータ</param> /// <returns> /// 戻り値 i:Keyをdat[i]で見つけた。 /// -1:見つからなかった /// </returns> int SequentialSearch(int n, int dat[], int key) { int i = 0; while (i < n && dat[i] != key) { i = i + 1; } if (i >= n) return -1; else return i; } int main() { int n = 10; int A[10] = { 6,3,2,9,5,4,10,1,8,7 }; int Key = 8; int res = SequentialSearch(n, A, Key); if (res == -1) printf("not found\n"); else printf("Data is found in A[%02d] = %02d\n", res, A[res]); }
はい。改良できるところどこよ?って思うかもしれないですが、ここです。
while (i < n && dat[i] != key)
(for文で書くとその改良が見えづらいですが)繰り返しの条件が、複合条件で「配列の境界チェック」AND「キーの発見チェック」になっています。
日本語で書くと、
iでループを回すときに「配列が大きさnをオーバーしない、かつ、データの[i]は探しているキーではない」間ループを回す
になっています。逆に言うとループを抜けるのは、「iが配列の大きさnを超えたとき」または、「キーが見つかった時」に配列の添え字を返す関数です。
それ以外はエラーとして―1を返します。
ここまでは、みんな大丈夫だと思います。
この条件をちょい簡単にする方法を考えてみます。
やることは簡単で、「必ずどこかでKeyが見つかるようにしておく」です。今のやり方はKeyが見つからないときがあるので、配列よりも添え字がオーバーしちゃわないか
こわごわ確認しながらサーチしてます。
それを、配列の最大値(最大サーチするとこの次(N+1とする))= Keyにしておくと、
▲ A[i] == Key | ・ res = i ▼ ▲ i == N | ・ A[0]~A[N-1]までにKeyはなかった(Nまでサーチしちゃってる) +--------------- | ・ A[i]でKeyは見つかった (0~N-1のどこかでKeyを見つけてある) ▼
と書くことができます。つまり、上の疑似言語の「i < N and」がいらなくなるね!
このアルゴリズムを、普通の線系探索を参考に疑似言語としてまとめて書いてみます。
〇 検索するデータ:Key
〇 データ列の数(配列数):N+1
〇 戻り値:
・データが見つかった時 :データの位置(index)
・データが見つからない時 :-1 (Not Foundの戻り値としてあり得ないindexを返す)
SequentialSearch()
・ i ← 0
・ A[N] = Key
■ A[i] != Key
| ・ i ← i + 1
■
▲ i == N ?
| ・ return -1
+---------------
| ・ return i
▼
なんか妙にすっきりしたね笑
最後にソース追加しておくよ
#include <iostream> /// <summary> /// 大きさnの配列datの中からkeyのデータを探す関数 /// </summary> /// <param name="n">配列のサイズ</param> /// <param name="dat">配列の名前</param> /// <param name="key">探すデータ</param> /// <returns> /// 戻り値 i:Keyをdat[i]で見つけた。 /// -1:見つからなかった /// </returns> int SequentialSearch(int n, int dat[], int key) { int i = 0; while (i < n && dat[i] != key) { i = i + 1; } if (i >= n) return -1; else return i; } /// <summary> /// 大きさnの配列datの中からkeyのデータを探す関数 /// </summary> /// <param name="n">配列のサイズ</param> /// <param name="dat">配列の名前</param> /// <param name="key">探すデータ</param> /// <returns> /// 戻り値 i:Keyをdat[i]で見つけた。 /// -1:見つからなかった /// </returns> int SequentialSearchSentinel(int n, int dat[], int key) { dat[n] = key; //データの最後=配列数と同じインデックスのとこ int i = 0; while (dat[i] != key) { i = i + 1; } if (i == n + 1) return -1; else return i; } int main() { int n = 10; //これはデータ数 //データ数+1の配列が必要 //一番後ろこの場合A[10]は、番兵さんが入るので空けとく int A[11] = { 6,3,2,9,5,4,10,1,8,7 }; int Key = 8; //int res = SequentialSearch(n, A, Key); int res = SequentialSearchSentinel(n, A, Key); if (res == n) printf("not found\n"); else printf("Data is found in A[%02d] = %02d\n", res, A[res]); }