Upload
others
View
9
Download
0
Embed Size (px)
Citation preview
1
アルゴリズムとデータ構造
第3回基本的なデータ構造(リスト、スタック、キュー)
2
今年度の授業計画回 日付 曜日 時間 担当者 内容
1 2019年4月4日 木曜日 2 喜田ガイダンス
2 2019年4月9日 火曜日 4 喜田アルゴリズムと計算量
3 2019年4月11日 木曜日 2 有村 基本的なデータ構造(1) リスト・スタック・キュー
4 2019年4月16日 火曜日 4 有村 基本的なデータ構造(2) ヒープ
5 2019年4月18日 木曜日 2 有村 基本的なデータ構造(3) 再帰アルゴリズム
6 2019年4月23日 火曜日 4 喜田探索のためのデータ構造(1) 二分探索木・AVL木
7 2019年4月25日 木曜日 2 喜田探索のためのデータ構造(2) 木の巡回
2019年4月30日 火曜日 祝日
2019年5月2日 木曜日 祝日
8 2019年5月7日 火曜日 4 有村 探索のためのデータ構造(3) 最適二分探索木
9 2019年5月9日 木曜日 2 有村 探索のためのデータ構造(4) ハッシュ
10 2019年5月14日 火曜日 4 喜田整列のアルゴリズム(1)
11 2019年5月16日 木曜日 2 喜田整列のアルゴリズム(2)
12 2019年5月21日 火曜日 4 喜田グラフとネットワークのアルゴリズム(1)
13 2019年5月23日 木曜日 2 喜田グラフとネットワークのアルゴリズム(2)
14 2019年5月28日 火曜日 4 有村 アルゴリズム関連の最近の話題
15 2019年5月30日 木曜日 2 有村 アルゴリズム関連の最近の話題(予備)
16 2019年6月4日 火曜日 4 有村・喜田 試験
出席・試験・成績について出席
毎回出席簿を付けます
欠席する場合は欠席届を提出してください
様式は工学部学生便覧の最終ページにあります
欠席届の無い場合は無断欠席とみなします
試験
科目最終回(16回目)に行う予定です
持ち込みは不可です
成績
シラバスの通りです(授業への参加態度10%,レポート20%,学期末試験70%)
3
ビッグデータ,人工知能,・・・,アルゴリズム
ワトソン君• IBMリサーチ (2011/02/16)• クイズ番組で人間に勝利!• 100万冊の本を読んで回答• 人工知能と自然言語,
アルゴリズム,検索の技術で
From http://www-06.ibm.com/ibm/jp/lead/ideasfromibm/watson/
米国の人気クイズ番組「ジョパディ!」のワトソン君クイズ王に挑戦中
AlphaGo• Google DeepMindが開発したコンピ
ュータ囲碁プログラム• 2015年10月にプロ囲碁棋士をハン
ディなしで破った• 2017年5月には世界トップ棋士であ
る柯潔(コ・ジェ)に勝利• DNNとモンテカルロ木探索
4
screenshot: DeepMind/YouTube
授業で学ぶデータ構造の範囲
機械語のデータ型
レジスタ値とその番地
基本データ型
char, int, large int, double, 構造データ型
配列(array),構造体(struct) 基本的データ構造
リスト(linked list),スタック(stack),待ち行列(queue)
先進的データ構造
二分探索木(binary search tree),平衡探索木(balanced search tree),ハッシュ表(hash table)
昔の言語も持っている型(C,Pascal)
機械語(型がない)
最近の言語(C++,Java, etc.)
この「アルゴリズムとデータ構造」で学ぶところ
「プログラミング」
5
6
データ構造
データ構造とは
セル(cell)の集合にある構造を与えたもの
データを格納する基本単位
Algorithms + Data Structures = Programs
Niklaus Wirth (二クラウス・ビルト)が書いた本の題名
Niklaus Wirth(1934-)はスイスの計算機科学者。コンピュータ言語Pascalの設計者。1984年にチューリング賞受賞。
抽象データ型 (Abstruct Data Type)とは?
データ型を,それに適用される一組の操作で抽象的に定めたもの.
データ操作
Linked List
CREATEINSERTDELETE. . . . .
• データ構造(のAPI)を「抽象データ型」ともいう.• データ構造には,「それは何か(What)」と「それをどのように実現す
るか?(How)」の二つの面がある.
• 現代的なプログラム言語やライブラリーはこの考え方に基づく.(例:C++,Java,Ruby, Python などなど)
7
重要
前回の内容:第2回 アルゴリズムと計算量
8
よいアルゴリズムとは?
いろいろな性能
計算時間
メモリサイズ
外部記憶への入出力数
ネットワークの通信量
ほかの要素
モジュラリティ Modularity再利用可能性 Reusability読みやすさ Readability移植しやすさ Portability ...
ここでは,計算時間とメモリサイズで測る
計算時間
メモリサイズ
2016/04/07
9
第3回基本データ構造
今日の内容:
リスト
配列/連結リスト/双連結リストによる実装
スタック
スタックの応用(再帰呼び出し)
キュー
ポイント
ポインタを用いたデータ構造
抽象データ型とその実装
11
リストとは? (抽象データ型として)
0個以上の要素を一列にならべたものA
12
空リスト:要素を含まないリストのこと リストの長さ:要素数n Ai: 最初から i 番目の要素(1 ≦ i ≦ n) リスト中の場所を指示するための「位置」をも
つ
太郎
二郎
花子
道子
A1 AnA2 A3リストA
重要
リストに対する操作
INSERT(x,p,L) : リストL(要素数n)の位置pの次
の位置に要素xを挿入
DELETE(p,L) : リストLの位置pの次の位置の次
の要素を削除
FIND(i,L) : リストLのi番目のセルの内容を返す
LAST(L) : リストLの最後のセルの位置を返す
PREVIOUS(p,L) : リストLにおいて、位置pの1つ
前のセルの位置を返す
13
太郎
二郎
花子
道子
14
リスト
リストとは要素を0個以上1列に並べたもの
(注意)リストは連結リストを指すことが多い
[リスト a0,a1,…,an-1の実現法]
1. 配列(array)
2. 連結リスト(linked list)
3. 双方向連結リスト(doubly linked list)
a0 a1 ・・・ an-1
n個の連続領域に格納
an-1 nulla1a0init ・・・
ポインタで次の要素の格納領域を指す
an-1 nulla1a0init ・・・null ・・・ final
ポインタで前後の要素の格納領域を指す
太郎
二郎
花子
道子
重要
復習:ポインタとは?
ポインタ (pointer)セルの位置を示すデータ
機械語レベルでは、セルの番地そのもの
プログラミングにおいては、その値を具体的に知る必要はない。
15
ポインタは番地
16
ポインタ p
132 値 x
5
C言語の場合
ポインタpが指す変数の値xを、x = *pで表す。
変数xを指すポインタpを p = &x で取り出せる。
レジスタレベルでみた計算機
17
リスト
リストとは要素を0個以上1列に並べたもの
(注意)リストは連結リストを指すことが多い
[リスト a0,a1,…,an-1の実現法]
1. 配列(array)
2. 連結リスト(linked list)
3. 双方向連結リスト(doubly linked list)
a0 a1 ・・・ an-1
n個の連続領域に格納
an-1 nulla1a0init ・・・
ポインタで次の要素の格納領域を指す
an-1 nulla1a0init ・・・null ・・・ final
ポインタで前後の要素の格納領域を指す
太郎
二郎
花子
道子
リスト:
配列(array)による実装
18
a1 a2 a3 a4
配列 int a[100]; % 100個の整数a[0],a[1],…,a[99]からなる配列
1. 配列(array) n個の連続領域に格納
a0 a1 ・・・ an-1
0 1 ・・・ n-1
n=4
リスト:連結リスト(linked list)による実装
要素を保持する「セル」をポインタでつないで,リストを表す.
途中への挿入削除を効率よくで行える
19
an nulla1-1head ・・・ ai-1 ai ・・・
a1 a2 a3 a4n=4
*セル = 区切られた箱や部屋のこと
セル*の構造体(C言語のコード)
typedef struct _cell {int element;struct cell *next;
} cell;
ダミーのセル
先頭(head)
aicellelement next
セル*(箱図)
要素 次のポインタ
20
C言語によるリストの定義(1/3)
連結リスト
typedef struct cell {int element;struct cell *next;
} cell;
cell *init=NULL; %空のリスト
void list_add(int x) { %整数要素xを先頭へ追加
cell *new=(cell *)malloc(sizeof(cell));new->element=x;new->next=init;init=new;
}
struct cell {・・・}: 構造体cellを・・・と定義
typedef ・・・ cell: ・・・をデータタイプcell型として定義
initはcell型データを指すポインタ型
sizeof(cell): cell型のデータサイズ(バイト)
malloc(n):nバイトのメモリーを確保
(cell *)malloc(n): 確保したnバイトの領域をcell型データ格納領域とみなす。
重要
aicell
要素
elementcellへのポインタ
next
22
C言語によるリストの定義(2/3)typedef struct cell {
int element;struct cell *next;
} cell;
cell *init=NULL; %空のリスト
void list_add(int x) { %整数要素xを先頭へ追加
cell *new=(cell *)malloc(sizeof(cell));new->element=x;new->next=init;init=new;
}
init3 6
NULL
element領域
next領域
list_add(5)を実行すると
new
①メモリの確保
new5
②elementの代入
init3 6
NULL
③nextにinitポインタをコピーnew
5
init3 6
NULL
④initにnewポインタをコピー
23
C言語によるリストの定義(3/3)
双方向連結リストtypedef struct cell {
int element;struct cell *prev;struct cell *next;
} cell;
cell *init=NULL; %空のリストcell *final=NULL;void list_add(int x) { %整数要素xを先頭へ追加
cell *new=(cell *)malloc(sizeof(cell));new->element=x;new->next=init;new->prev=NULL;if(init==NULL) final=new;else init->prev=new;init=new;
}
cell型は1つ前のデータを指すポインタprevももつ
最後尾のデータを指すポインタfinalも必要
先頭に追加する場合は1つ前のデータはなし
最初に格納されたデータが最後尾のデータ(finalが指すデータ)となる
2つ目以降に格納されたデータはprevポインタを新しい先頭データを指すように更新する
重要
24
連結リストが得意な処理(挿入)INSERT(x,p,L) : リストL(要素数n)の位置pの次の位置に要素xを挿入
a0 a1 ・・・ ai ・・・ an-1ai-1
p
a0 a1 ・・・ ai ・・・ an-1ai-1 x最悪/平均時間計算量はΘ(n)
配列の場合
p
x
an-1 nulla1a0init ・・・ ai-1 ai ・・・
an-1 nulla1a0init ・・・ ai-1 ai ・・・
連結リストの場合
最悪/平均時間計算量はΘ(1)
an-1 nulla0
init・・・null ・・・
finalai-1 ai ・・・・・・
an-1 nulla0
init・・・null ・・・
finalai-1 ai ・・・・・・
p
x
双方向連結リストの場合
最悪/平均時間計算量はΘ(1)
重要
考えてみよう
25
連結リストが得意な処理(削除)DELETE(p,L) : リストL(要素数n)の位置pの次の位置の次の要素を削除
a0 a1 ・・・ ai ・・・ an-1ai-1
p
a0 a1 ・・・ ai+1 ・・・ an-1ai-1
最悪/平均時間計算量はΘ(n)
配列の場合
an-1 nullai-1a0init ・・・ ai ai+1 ・・・
p連結リストの場合
最悪/平均時間計算量はΘ(1)
ai-1 ai ai+1 ・・・・・・
p双方向連結リストの場合
最悪/平均時間計算量はΘ(1)
ai+1
an-1 nullai-1a0init ・・・ ai+1 ・・・
・・・・・・
ai-1 ai+1 ・・・・・・・・・・・・
26
配列が得意な処理(i番目の要素へのアクセス)FIND(i,L) : リストL(要素数n)のi番目のセルの内容を返すLAST(L) : リストL(要素数n)の最後のセルの位置を返すPREVIOUS(p,L) : リストL(要素数n)において、位置pの1つ前のセルの位置を返す
a0 a1 ・・・ ai ・・・ an-1ai-1
配列の場合
an-1 nullai-1a0init ・・・ ai ai+1 ・・・
連結リストの場合
双方向連結リストの場合
ai+1aFIND(i,L) : Θ(1)LAST(L) : Θ(1)PREVIOUS(p,L) : Θ(1)
p
FIND(i,L) LAST(L)PREVIOUS(p,L)
FIND(i,L) : Θ(n), LAST(L) : Θ(n), PREVIOUS(p,L) : Θ(n)
p
連続領域であるためi番目の要素や前後の要素に1ステップでアクセス可能
PREVIOUS(p,L) LAST(L)
=
FIND(i,L)=
an-1 nulla0
init・・・null ・・・
finalai-1 ai ・・・・・・
p
FIND(i,L) : Θ(n), LAST(L) : Θ(1), PREVIOUS(p,L) : Θ(1)PREVIOUS(p,L) LAST(L)FIND(i,L)
=
27
配列による連結リストの実現
a1 a2 ・・・a0 an-1 an-2
2 4 6 9 7 ・・・1 -1 m-1 5 -1
0 1 2 3 4 5 6 m-3 m-2 m-1
free_init 0 init 3
メリット•メモリー確保、解放の時間を節約できる。•メモリー使用量を制御できる。•ガベージコレクションの必要がない。
ちょっとむずかしい!
スタックとキュー
特別な(制限された)リストのいろいろ
28
29
スタック(stack)スタックとは
要素の挿入、削除がいつも先頭からなされるリスト
LIFO(last-in-fast-out)[基本操作]TOP(S) スタックSの先頭の位置を返すPOP(S) スタックSの先頭の要素を削除PUSH(x,S) スタックSの先頭に要素xを挿入
a0
a1
a2 PUSH(a2,S)
a0
a1
a2
a0
a1
a2POP(S)
TOP(S)
重要
30
スタックの実現法
a0 a1 ・・・ ・・・ai
i
すべての操作の時間計算量はΘ(1)
配列による実現
a0 nullaitop ・・・連結リストによる実現
すべての操作の時間計算量はΘ(1)
top
S
=TOP(S)
a0 a1 ・・・ ・・・ai
i+1top
S ai+1
PUSH(ai+1,S) POP(S)
ai-1
TOP(S)=
a0 nullaitop ・・・ai-1
ai+1
PUSH(ai+1,S) POP(S)
重要
31
スタックの応用例(関数呼び出し)プログラム(階乗n!の計算)
int factorial(int n) {if(n==1) return 1;else return n*factorial(n-1);
}
実行コードシークエンス
1: if n≠1 then goto 32: return 13: r=factorial(n-1)4: return n*r
局所変数n 3
実行コード1:2:3:4:
3実行アドレス
n 2
1:2:3:4:
n=3, 戻り:4スタック
n
1:2:3:4:
n=3, 戻り:4n=2, 戻り:4
n=3, 戻り:4スタック
3
3
1:2:3:4: 4
1
2
n 2
1:2:3:4: 4
n
factorial(3) factorial(2)
factorial(1)
3
6
待ち行列(キュー)とは?
要素の挿入は最後尾、削除は先頭からなされるリスト
FIFO(fast-in-fast-out)ともいう
32
[基本操作] TOP(Q) キューQの先頭の位置を返す
ENQUEUE(x,Q) 要素xをキューQの最後尾に入れる
DEQUEUE(Q) 先頭の要素をキューQから除く
「エンキュー」、「デキュー」と読む
A 1 , A 2 , . . . , A nC D E FA B
G H I
重要
33
待ち行列(キュー, queue)待ち行列(キュー)とは
要素の挿入は最後尾、削除は先頭からなされるリスト
FIFO(fast-in-fast-out)
[基本操作]TOP(Q) キューQの先頭の位置を返すENQUEUE(x,Q) 要素xをキューQの最後尾に入れるDEQUEUE(Q) 先頭の要素をキューQから除く
a0 a1 a2ENQUEUE(a2,Q)
a0 a1 a2
TOP(Q)
a0 a1 a2
DEQUEUE(Q)
34
キューの実現法
・・・ ・・・a0
j
すべての操作の時間計算量はΘ(1)
配列による実現
ai nulla0front ・・・
連結リストによる実現
front
Q
TOP(Q)
ENQUEUE(ai+1,Q)
a1
TOP(Q)=
aia0front ・・・a1 ai+1
ENQUEUE(ai+1,S)
ai ・・・
=
(j+i)%nrear0 n-1
DEQUEUE(Q)
a1
・・・ ・・・a0
front
Q ai ・・・
(j+i+1)%nrear0 n-1
a1 ai+1
・・・ ・・・
(j+1)%nfront
ai ・・・
(j+i+1)%nrear0 n-1
a1 ai+1
j
rear
null rear
aifront ・・・a1 ai+1 null rear
DEQUEUE(Q)すべての操作の時間計算量はΘ(1)
A4 A5 A1 A2 A3
0 1 2 3 4 5
frontrear
配列で巡回リストを表わす. キューの長さ n が限定された場合
先頭と末尾がつながって,輪になったリスト
要素の位置を,n の剰余演算で決定.
配列によるキューの実現
front から i 番目の要素 = Element[ (front + i) mod n ]
Element
ex. Elem[(head + 5) mod n] (head = 1 and n = 6)= Elem[7 mod 6] = E[1]
35
考えてみよう