Transcript 用語編

情報システム基盤学 基礎1
アルゴリズムとデータ構造
Elements of Information
Systems Fundamentals1
アルゴリズムとデータ構造 第3回
前半部
1
目次:

基本的なデータ構造




線形リスト
スタック
キュー
ハッシュテーブル
初歩的過ぎる ので 教科書を自分で読むこと.
(平均的な効率の解析は難しい  MIT本を見よ).
2
線形リスト
直観
データを一列に並べて
覚えたもの
データを一列に並べたもの
(データを線形順序に並べたもの)
9
head
16
4
線形リストの例
各要素:
前の要素へ
のポインタ
1
次の要素へ
のポインタ
prev
next
16
1つの要素はprev,key,nextの組
prev key next
次/前の要素がなければNULL
key
NULL:
データ
3
様々な線形リスト
1. 単一方向の線形リスト
head
9
16
4
1
16
4
1
9
16
4
1
2. 双方向の線形リスト
head
9
3. ソートされた双方向の線形リスト
head
1
4
4. ソートされてない双方向の線形リスト
head
9
16
4
線形リストに対する命令
探索(search)
与えられた値 k をもつ要素を返す
挿入(insert)
与えられた値 k をもつ要素を
線形リストの先頭へ挿入する
削除(delete)
与えられた値 k をもつ要素を削除する
5
探索
値 k が与えられたとき, 値 k をもつ要素返す
1. 値 k をもつ要素を見つける
先頭から順々に調べていく(線形探索)
2. 見つけた要素を返す, なかったらNULLを返す
例
下記のリストに対して
値 4 が与えられたならば, 答えとして
head
線形探索
9
16
4
4
を返す
1
6
探索の擬似コード
LIST-SEARCH(k)
1 x = head
2 while x ≠ NULL and key[x] ≠ k
ポインタ = 要素の名前
3
do x ← next[x]
と解釈すればプログラミング
4 return x
しやすいかもしれません
head: 線形リストの先頭要素
x: 線形リスト上の1要素を表現
key[x]: 要素 x がもつ値
計算時間:
next[x]: 要素 x の次の要素
prev[x]: 要素 x の前の要素
head
9
16
O(n) 時間
4
1
7
挿入
要素 x が与えられたとき, x を線形リストの先頭に加える
例
下記のリストに対して値 25 をもつ要素が与えられたとすると
head
9
16
4
1
挿入
head
25
9
16
4
1
8
挿入の擬似コード
LIST-INSERT(x)
1 next[x] ← head
2 if head ≠ NULL
3
then prev[head] ← x
4 head ← x
5 prev[x] ← NULL
x
head
head: 線形リストの先頭要素
x: 線形リスト上の1要素を表す
key[x]: 要素 x がもつ値
next[x]: 要素 x の次の要素
prev[x]: 要素 x の前の要素
計算時間:
O(1) 時間
9
削除
要素 x が与えられたとき, x を削除
例
下記のリストに対して値 4 をもつ要素が与えられたとすると
head
25
9
16
4
1
削除
head
25
9
16
1
10
削除の擬似コード
LIST-DELETE(x)
1 if prev[x] ≠ NULL
2 then next[prev[x]] ← next[x]
x
3 else head ← next[x]
head
4 if next[x] ≠ NULL
5 then prev[next[x]] ← prev[x]
head: 線形リストの先頭要素
x: 線形リスト上の1要素を表す
key[x]: 要素 x がもつ値
next[x]: 要素 x の次の要素
prev[x]: 要素 x の前の要素
計算時間:
O(1) 時間
11
目次: 今日やること

基本的なデータ構造





線形リスト
スタック
キュー
ハッシュテーブル
課題
12
スタック(Stack)
もっとも基本的なデータ構造のひとつ
木の探索を表現(深さ優先探索)
Last-in first-out(LIFO)
13
スタックを例えると
出入口が1つだけの満員電車
電車の中へ
出入口
A
B
C
D
電車(だと思ってください)
14
スタックを例えると
最後に入った人が最初に出る仕組み
出入口が1つだけの満員電車
電車の外へ
出入口
D
C
B
電車(だと思ってください)
A
15
スタックのイメージ
データの流れ
縦長の箱に, データを一列に入れていく
注意:データの出入口は1つだけ
5
12
30
提供されている命令
スタック
プッシュ(push): データの追加
ポップ(pop): データの削除
例
13
8
10
8
10
10を
追加
10
8を
追加
13を
追加
8
10
削除
10
削除
16
プッシュ命令
与えられた要素 x をスタックの先頭に追加
要素が1つ
だけ増える
5
12
30
スタック
値125 をもつ
要素をプッシュ
125
5
12
30
スタック
17
プッシュ命令の擬似コード
※線形リストでスタックが実装されていたと仮定
x
PUSH(x)
1 next[x] ← top
2 if top ≠ NULL
3 then prev[top] ← x
4 top ← x
5 prev[x] ← NULL
線形リストの挿入と同じ
125
top
top
5
12
30
125
5
12
30
スタック
スタック
18
ポップ命令
スタックの先頭の要素を削除
※要素を選んだりはしない
5
12
30
スタック
要素が1つ
だけ減る
ポップ
12
30
スタック
19
ポップ命令の擬似コード
※線形リストでスタックが実装されていたと仮定
POP(x)
1 if top ≠ NULL
2 then top ← next[top]
3
prev[top] ← NULL
top
5
12
30
スタック
top
ポップ
12
30
スタック
20
スタックと木の探索の関連
次の操作をやってみる
深さ優先探索で,
1. 頂点 v へ初めて訪れたとき, v をプッシュ
2. 頂点 v を最後に訪れるときポップ
Start
End
1
2
3
7
6
5
4
4
5
6
2
3
1
7
21
木構造
木構造:上から下へ枝分かれした構造
r
:頂点(点)
:辺
a
e
b
f
k
g
l
n
h
c
i
d
j
m
木(根つき木)の例
:根
親と子
葉と内点
祖先と子孫
頂点 x を根とする部分木
深さ
22
木構造
木構造:上から下へ枝分かれした構造
:頂点(点)
r
:辺
e
k
a
b
f
h
g
l
n
c
i
d
j
:根
親と子
隣接する2頂点のうち,
・根に近い方 → 親
・根から遠い方 → 子
m
木(根つき木)の例
23
目次: 今日やること

基本的なデータ構造





線形リスト
スタック
キュー
ハッシュテーブル
課題
24
キュー(Queue)
もっとも基本的なデータ構造のひとつ
待ち行列を表現
First-in first-out(FIFO)
待っている人の列
レジ
25
キューのイメージ
入口
横長の箱にデータを出し入れ
出口
3
※データの入口と出口の2つがある
23 20
データの流れ
提供されている命令
エンキュー(enqueue): データの追加
デキュー(dequeue): データの削除
20
20をエン
キュー
23 20
23をエン
キュー
3
3をエン
キュー
23 20
3
デキュー
23
3
デキュー
26
エンキュー(Enqueue)
与えられた要素 x をキューの最後尾に追加
tail
head
14
5
20
値34をもつ要素を
エンキュー
tail
head
34 14
5
20
27
エンキューの擬似コード
※線形リストでスタックが実装されていたと仮定
x: 挿入する要素
tail
head
x
ENQUEUE(x)
1 if tail = NULL
2 then head ← x
3
4
5
next[x] ← NULL
else prev[tail] ← x
next[x] ← tail
6 prev[x] ← NULL
7 tail ← x
34
14
5
20
値34をもつ要素を
エンキュー
tail
head
34 14
5
20
28
デキュー(Dequeue)
キューの先頭の要素を削除
tail
head
34 14
5
20
デキュー
tail
head
34 14
5
20
29
デキューの擬似コード
※線形リストでスタックが実装されていたと仮定
tail
DEQUEUE()
1 if head = tail
2 then head ← tail ← NULL
3 else head ← prev[head]
4
next[head] ← NULL
head
34 14
5
20
デキュー
tail
head
34 14
5
20
30
目次: 今日やること

基本的なデータ構造





線形リスト
スタック
キュー
ハッシュテーブル
課題
31
ハッシュテーブル (Hash Table)
もっとも基本的なデータ構造のひとつ
ハッシュ
テーブル
欲しいデータを素早く検索
0
1
2
3
4
比較的、省スペースで実現できる
(検索スピードとスペースはトレードオフ)
正の整数の集合
3
1
0
5
7
11
6
12
2
9 4
8
10
12
2
4
8
9
32
ハッシュの概要 1/2
やりたいこと:集合の一部を保存したい
線形リストを利用すれば実現可能
正の整数の集合
0
6
3
1
10
5
9
7
11
8
2
4
12
挿入: O(1) 時間
削除: O(1) 時間
探索: O(n) 時間
スペース: 保存する整数分だけ
もう少しスペースを使ってもいいから、
効率よく探索を行いたいなぁ・・・
保存する整数の集合は可変なので、
挿入・削除も効率よくやりたいな・・・
(緑部分は可変)
ハッシュ
33
ハッシュの概要 2/2
ハッシュテーブル
と呼ぶ
テーブル
基本方針: テーブルに要素を保存する
正の整数の集合
1
0
6
3
10
5
9
7
11
8
2
4
12
(配列だと思えば良い)
ハッシュ関数
と呼ぶ
k
任意に取り出し
てきたk について
考えます
関数 h
h(k)
0
1
2
3
4
12
2
4
ハッシュ値
と呼ぶ
気になること
ハッシュ関数の決め方
ハッシュ値が等しいとき(衝突)はどうする?
8
9
h(k)
k
34
ハッシュ関数
様々なハッシュ関数が知られている
良いハッシュ関数
テーブル上に整数を一様にばらまく(一様ハッシュ関数)
計算が簡単
今回は、除算法(division method)を用いる
除算法では以下のハッシュ関数を使用
h(k) = k mod p
k: 保存したい整数
p: 自分で決める値(素数がよい)
テーブルサイズは p – 1 とする
35
ハッシュテーブルの例
ハッシュ
テーブル
0
190
下記の整数をハッシュテーブルで保存する
1
77
{3, 5, 77, 109, 190, 245, 832, 852, 9924, 10346} 2
3
3
4
ハッシュ関数は下記のように定義
5
5
6 9924
p =19 としてます
h(k) = k mod 19
7
8
9
このとき, 各整数のハッシュ値は次の通り
10 10346
h(3) = 3, h(5) = 5, h(77) = 1, h(109) = 14,
11
h(190) = 0, h(245) = 17, h(832) = 15,
12
h(852) = 16, h(9924) = 6, h(10346) = 10
13
14 109
15 832
ここで, もし, 整数 25 を挿入したいとしたら・・・
16 852
h(25) = 6 となり, すでに 9924 が格納されている 17 245
18
(これを衝突と呼ぶ)
ど、どうしよう。。。
36
ハッシュ
テーブル
0
190
1
77
2
3
3
4
5
5
6 9924
7
8
9
10 10346
11
12
13
14 109
15 832
16 852
17 245
18
衝突回避 その1:
チェイン法(Chaning)
456
リストを使って衝突を回避
各スロットに複数の要素を対応させる
25
44
各スロットについて線形リストを保持する
例
h(25) = 6: スロット6で衝突 → スロット6のリストへ追加
h(456) = 0: スロット0で衝突 → スロット0のリストへ追加
h(44) = 6: スロット6で衝突 → スロット6のリストへ追加
デメリット: 使用していないスペースがまだあるのに,
他のスペースを使ってしまう
37
ハッシュ
テーブル
0
190
1
77
2
3
3
4
5
5
6 9924
7
25
8
9
10 10346
11
12
13
14 109
15 832
16 852
17 245
18
91
衝突回避 その2:
線形探査(Linear Probing)
線形探査の流れ
25
1. スロット i で衝突が発生
2. スロット i + 1 をチェック
3. スロット i + 1 が空きならば, そこへ整数を保存、終了
4. そうでないならば, i をインクリメントして 1. へ戻る
例
h(25) = 6: スロット6で衝突 → スロット7へ
スロット7が空きなので、そこへ追加
91
h(91) = 15: スロット15で衝突 → スロット16へ
スロット16で衝突 → スロット17へ
スロット17で衝突 → スロット18へ
スロット18が空きなので、そこへ追加
デメリット: クラスタができてしまい、
追加・探索が極端に遅くなるかもしれない
38
Note: 本スライドは山中克久 助教(当時)が
2010年度に作成したものを,今回,大森が
改訂したものです.
2013.4.25.記
39