情報システム基盤学 基礎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
© Copyright 2025 ExpyDoc