アルゴリズムとデータ構造

アルゴリズムと
データ構造
第3回
探索
探索

探索とは
 データの集合から、目的とする値を持った要素を
探し出すこと

探し出す値の項目:キー
 データの一部
 キーの指定方法はさまざま


一致、範囲指定、近接など
探索失敗する(見つからない)こともあり
探索とコスト

探索アルゴリズム
 用途、目的、実行速度、対象のデータ構造などに
より使い分け
 探索以外にどのような操作を行うかも考慮
単に見つかればOK→計算時間が短ければよい
 データの追加や削除もあり→追加や削除のコストがあ
まり大きくならないように

線形探索

線形探索(逐次探索)とは
 目的とするキー値と一致するまでデータを先頭か
ら順に探索
 探索の終了
探索すべき値と等しい要素があった場合
 探索すべき値と等しい要素がなく末端を通り越した場合

 探索のコスト

n項目あれば平均n/2回
線形探索
7を探索
6
4
3
7
1
9
8
1
9
8
探索成功
5を探索
6
4
3
7
探索失敗
線形探索

線形探索プログラム(無限ループ版)
int search(const int a[], int n, int key) {
int i = 0;
while (1) {
if (i == n) return (-1);
if (a[i] == key) return (i);
i++;
}
}
/* 無限ループ */
/* 探索失敗 */
/* 探索成功 */
/* 次の要素 */
線形探索

線形探索プログラム(for版)
int search(const int a[], int n, int key) {
int i;
for (i = 0; i < n; i++)
if (a[i] == key) return (i);
return (-1);
}
/* 探索成功 */
/* 探索失敗 */
線形探索

番兵法
 データの末尾まで探索したかのチェックを省略
 末尾にキー値を格納:番兵
データ内に一致する値がなくても必ず番兵と一致
 一致位置が末尾なら探索失敗と判断

線形探索

番兵法による線形探索(無限ループ版)
int search(const int a[], int n, int key) {
int i = 0;
a[n] = key;
/* 末尾に番兵を配置 */
while (1) {
/* 無限ループ */
if (a[i] == key) break;
/* 探索成功 */
i++;
/* 次の要素 */
}
return (i == n ? -1 : i);
/* 末尾なら探索失敗 */
}
線形探索

番兵法による線形探索(for版)
int search(const int a[], int n, int key) {
int i;
a[n] = key;
/* 末尾に番兵を配置 */
for (i = 0; ; i++)
/* ループ終了条件不要 */
if (a[i] == key) break;
/* 探索成功 */
return (i == n ? -1 : i);
/* 末尾なら探索失敗 */
}
2分探索

2分探索とは
 要素があらかじめソート(整列)されているデータ
から効率よく探索を行うアルゴリズム
 中央の値に着目し、その値より大きいか小さいか
で探索範囲を半分に絞り込み
 探索のコスト

n項目あれば平均log(2)n回
2分探索
39を探索
5
7
15
28
29
31
39
58
< 39探索成功
6を探索
5
7
15
< 6 探索失敗
>6 >6
28
29
31
>6
39
58
68
70
95
> 39
68
70
95
2分探索
pl
39を探索
5
7
15
28
29
pc plplpc pr
pc
31
68
39
58
< 39探索成功
pl pc
plpr
pl pc
plpc pr
6を探索
5
7
15
< 6 探索失敗
>6 >6
28
pr
pc
29
31
>6
pr
70
95
> 39
pr
39
58
68
70
95
計算量

アルゴリズムの性能を客観的に評価する尺度
 時間計算量:実行に要する時間を評価
 領域計算量:実行に要するメモリ領域を評価

オーダー
 時間計算量を表現する方法
 各部の計算量のうち、より大きい計算量が支配
線形探索:O(n)
 2分探索:O(log(2)n)

ハッシュ法

ソート済み配列の操作
 データの追加:格納位置からあとの配列をすべて
1つずつ移動し、格納位置をあける必要あり
 データの削除:削除位置からあとの配列をすべて
1つずつ移動し、削除位置を埋める必要あり

ハッシュ法とは
 格納する値を元に、簡単な計算で格納位置を求
め、効率よく格納、探索を行う方法
ハッシュ法

ハッシュ法の原理
 キー値からハッシュ値(配列の場合は添え字)を
求める:ハッシュ関数
キー値
5
6
14
20
29
34
37
51
69
75
13で割った剰余
5
6
1
7
3
8
11
12
4
10
5
6
7
8
9
10
11
12
5
6
20
34
-
75
37
51
0
1
2
3
4
-
14
-
29
69
ハッシュ法

ハッシュ法による要素の挿入
0
1
2
3
4
-
14
-
29
69
5
6
7
8
9
10
11
12
5
6
20
34
-
75
37
51
5
6
7
8
9
10
11
12
5
6
20
34
35
75
37
51
35を挿入:35=13x2+9 ハッシュ値9
添え字9の位置に挿入
0
1
2
3
4
-
14
-
29
69
ハッシュ法

衝突
 格納すべきバケットが重複
 対処法
チェイン法:同一ハッシュ値を持つ要素を線形リストで
管理
 オープンアドレス法:空きバケットが見つかるまでハッ
シュの繰り返し

ハッシュ法

チェイン法(オープンハッシュ法)
0
1
13
14
2
3
4
5
6
7
8
29
69
17
5
6
33
20
46
34
69
17を挿入
33を挿入
46を挿入
33を削除
ハッシュ値7
ハッシュ値4
20
9
10
11
12
75
37
51
ハッシュ法

オープンアドレス法(クローズドハッシュ法)
 衝突が起きたら再ハッシュ
ハッシュ関数のほかに再ハッシュ関数
 空きバケットを求め順にたぐる→線形探査法

 要素削除のときの問題

すでに削除されたのと同じ要素が再ハッシュで配置さ
れていた場合、たどり着けない→削除済みフラグ
ハッシュ法

オープンアドレス法(クローズドハッシュ法)
0
1
2
3
4
-
14
-
29
69
18を挿入
5
6
7
8
9
10
11
12
5
6
18
-
34
-
75
37
51
18
18
18
衝突 衝突
再ハッシュ値7:添え字7の位置に挿入
ハッシュ値5:添え字5の位置に挿入
再ハッシュ値6:添え字6の位置に挿入