• 検索結果がありません。

アルゴリズムとデータ構造 第 6 回 :  探索問題に対応する

N/A
N/A
Protected

Academic year: 2021

シェア "アルゴリズムとデータ構造 第 6 回 :  探索問題に対応する"

Copied!
28
0
0

読み込み中.... (全文を見る)

全文

(1)

アルゴリズムとデータ構造 第 6 回 :  探索問題に対応する

データ構造 (2)

担当 :  上原隆平 (uehara)

2015/04/22

(2)

内容

• スタック (stack):  最後に追加されたデータが最

初に取り出される

• 待ち行列 / キュー (queue):  最初に追加された

データが最初に取り出される

• ヒープ (heap):  蓄えられたデータのうち小さい

ものから順に取り出される

(3)

スタック (STACK)

配列による実装

連結リストによる実装

(4)

スタック (stack)

• 最後に追加されたデータが最初に取り出 される構造 (LIFO: Last in, first out)

• 可能な操作

– push:  新たなデータを追加する

– pop:  データを取り出す

• ポインタ

– top: stack の先頭

( 次に要素を入れる場所 ) を指す

stack

push 3;

push 4;

push 5;

pop;

pop;

push 6;

pop;

3 4 5 6

 5

 4

 6 top       

(5)

配列を使った stack の実装

• データの格納 : push(x)

• データの取り出し : pop()

• 実行時エラー対策

オーバーフロー

: top == size(stack)

のときに

push(x) –

アンダーフロー

: top == 0

のときに

pop(x) 

stack[top]=x;

top=top+1;

top=top‐1;

return stack[top];

(6)

int stack[MAXSIZE];

int top = 0;

void push(int x){

if(top < MAXSIZE){

stack[top] = x; top = top + 1; 

} else 

printf("STACK overflow");

}

int pop(){

if(top > 0){

top = top ‐ 1; return stack[top];

} else

printf("STACK underflow");

}

配列を使った stack の実装

(7)

list_t* push(list_t *top,int x){

list_t *ptr;

ptr=(struct list_t*) malloc(sizeof(list_t));

ptr‐>data=x; ptr‐>next=top; return ptr;

}

list_t* pop(list_t *top){

list_t *ptr; ptr=top‐>next; free(top); return ptr;

}

typedef struct{

int data; struct list_t *next;

}list_t;

連結リストによる stack の実装

• 利点 :  スタックのサイズを宣言する必要無し

ガベージコレクションのある言語では必要無し

(8)

待ち行列 / キュー (QUEUE)

(9)

0 1 2 3 4 ... MAXSIZE-1 配列queue 35 29 87

データの head tail データの

取り出し (前部) (後部) 格納

データを

queue[head+1]

から

queue[tail]

までに蓄える

待ち行列 / キュー (queue)

• 最初に追加されたデータが最初に取り出

される (FIFO: first in, first out)

(10)

x:データ queue

head tail

void append(int x){

tail = tail + 1;

queue[tail] = x;

}

配列による queue の単純な実装 :

データの格納

(11)

取り出すべきデータ

head tail queue

int get(){

head = head + 1;

return queue[head];

}

配列による queue の単純な実装 :

データの取り出し

(12)

単純な queue 実装の問題 :

使っているうちに無駄ができる

• 以下のように queue を 使うと、配列はどう使わ れる ?

void append(int x){

tail = tail + 1;

queue[tail] = x;

}

int get(){

head = head + 1;

return queue[head];

int queue[MAX_SIZE]; } int head, tail;

void main(){

head=0; tail=0;

append(3); get();

append(4); get();

}

append(3) 3

head

tail get()

append(4) 4

もう

2

度と使えない

無駄

(13)

tail head

head tail tail

tail head

head

void append(int x){

tail = (tail + 1) % MAXSIZE;

queue[tail] = x;

}

int get(){

head = (head + 1) % MAXSIZE;

return queue[head];

}

解決 :  配列を環状に使う

端まで行ったら

0

に戻る

端まで行ったら

0

に戻る

(14)

full

のとき

t h head==tail

empty

のとき

:

h t head==tail

環状利用の問題 :

満 (full) と空 (empty) が区別不能

どちらの場合も

head==tail

で区別できない

get()

append

(15)

void append(int x){

tail = (tail + 1) % MAXSIZE;

queue[tail] = x;

if(tail == head) printf("Queue Overflow ");

}

int get(int x){

if(tail == head) printf("Queue is empty ");

else {

head = (head + 1) % MAXSIZE;

return queue[head];

} }

解決 : append 時に tail==head となっ

た時を full とする

(16)

データの挿入:リストの末尾から

tail

ポインタ データの取り出し:リストの先頭から

head

ポインタ

head

tail head

tail

x

データの 取りだし

データの 挿入

連結リストによる queue の実現

プログラムは練習問題とする

(17)

ヒープ (HEAP)

配列を用いた単純な実装

2分木の考え方を用いた配列による実装

(18)

ヒープ (heap)

• データの格納ができる

• 最小 ( または最大 ) の要素から順に

取り出される

(19)

ヒープの実現 (1):

配列を使った単純な実現

1

次元配列

heap[]

とデータ数 を表す変数

top

を用意

初期設定

:  top = 0

データの格納

:

heap[top] = x;

top = top + 1;

最小要素の取り出し

:

配列

heap[]

の中で最小の 要素

heap[m]

を求め、これ を出力した後、右端の要素

heap[top‐1]

heap[m]

の 位置に移し、

top

の値を

1

減 らす

0 1 2 m top heap

最小要素

m = 0;

for(i=1; i<top; i++)

if(heap[i] < heap[m]) m = i;

x = heap[m];

heap[m] = heap[top‐1];

top = top ‐ 1;

return x;

(20)

配列による単純な実現の問題 : データの取り出しが遅い

• 格納 : O(1)

• 取り出し : O(n)

m = 0;

for(i=1; i<top; i++)

if(heap[i] < heap[m]) m = i;

x = heap[m];

heap[m] = heap[top‐1];

top = top ‐ 1;

return x;

heap[top++]=x

(21)

18 ... level 0

25 33 ... level 1

26 31 35 42 ... level 2 節点

28 29 ... level 3

根:親のない節点 葉:子のない節点

どの節点も

2

個以内の子をもつとき

2

分木という

根からの距離

(枝の個数)を レベルという

2 分木による heap の実現

(22)

1.

根に番号1を割り当てる

2.

番号

の節点の左の子には

2

×

i

、右の子には

2

×

i+1 

の番号 を割り当てる.

3.

節点の個数

n

を超える番号をもつ節点は存在しない.

4.

どの枝についても,親には子以下のデータを蓄える

i

2×i 2×i+1

どの節点についても根から唯一のパスが存在して その長さは

O(log n)

Heap を実現する 2 分木の性質

(23)

10 1

13 2 11 3

15 4 14 5 12 6 18 7 21 8 22 9

1 2 3 4 5 6 7 8 9 10 13 11 15 14 12 18 21 22

連結リストを使うことなく配列で表現可能

:

Heap を実現する 2 分木の性質 :  例

1.

根に番号

1

を割り当てる

2.

番号

の節点の左の子に

2

×

i

、右の子には

2

×

i+1 

の番号を割り当てる.

3.

節点の個数

n

を超える番号 をもつ節点は存在しない.

4.

どの枝についても,親には

子以下のデータを蓄える

(24)

(1)

データを節点

n+1 

に格納(配列の

n+1 

番目)

(2)

根に向けて上へ上へとたどり、

if 

親のデータ > 子のデータ

then

親子のデータを交換

10 1

13 2 11 3

15 4 14 5 12 6 18 7 21 8 9 22 8 10

8 1

10 2 11 3

15 4 13 5 12 6 18 7 21 8 9 22 14 10

n+1

番目の節点から根までの経路上の要素を大きさの順に並べ る。このとき残りの部分との間で矛盾を引き起こすことはない。

Heap へのデータの追加

(25)

void pushHeap(int x){

int i, j;

if(++n >= MAXSIZE)

stop("Heap Overflow");

else{

heap[n] = x;

i=n; j=i/2;

while(j>0 && x < heap[j]){

heap[i] = heap[j];

i=j; j=i/2;

}

heap[i] = x;

} }

Heap へデータを追加する プログラム

10 1

13 2 11 3

15 4 14 5 12 6 18 7 21 8 9 22 8 10

8 1

10 2 11 3

15 4 13 5 12 6 18 7 21 8 9 22 14 10

(26)

(1)

根のデータを最小データとして取り出す

(2)

番号

n

の節点のデータを根に移動

(3)

根から葉に向けてたどる

このとき、

2

個の子節点のうちデータの小さい方を 選び、それが親のデータより小さいときはデータを 交換する。

10 1

13 2 11 3

15 4 14 5 12 6 18 7 21 8 22 9

11 1

13 2 12 3

15 4 14 5 22 6 18 7 21 8

最小データ

Heap:  最小要素の取り出し

(27)

int* deleteMin(int *heap, int n){

int x, i, j, t;

if(n == 0) stop("Heap Underflow");

else{

heap[1]=heap[n‐‐];

for(i=1;i*2<=n;i=j){

j=i*2;

if(j+1<=n && heap[j]>heap[j+1]) j=j+1;

if(heap[i]<=heap[j]) break;

else {

t=heap[i]; heap[i]=heap[j]; heap[j]=t;

} }}

return heap;}

Heap の最小要素を取り除く

プログラム

13 2 11 310 1

15 4 14 5 12 6 18 7 21 8 22 9

2

分木の

i

に子がある

&&

右に最小値の可能性あり

子よりも小さい

(i

番目

)

と子

(j

番目

)

を入れ替え

11 1

13 2 12 3

15 4 14 5 22 6 18 7 21 8

(28)

2 分 heap の計算時間

• heap のサイズを n とする

– 追加 : O(log n)

– 取り出し : O(log n)

どちらの操作も明らかにヒープの 深さに比例する時間で実行

ヒープの深さは

O(log n)

参照

関連したドキュメント

1) Manual of symbols and terminology for physicochemical quantities and units - Appendix II definitions, terminology and symbols in colloid and surface chemistry, Part

自ら将来の課題を探究し,その課題に対して 幅広い視野から柔軟かつ総合的に判断を下す 能力 (課題探究能力)

現実感のもてる問題場面からスタートし,問題 場面を自らの考えや表現を用いて表し,教師の

WAKE_IN ピンを Low から High にして DeepSleep モードから Active モードに移行し、. 16ch*8byte のデータ送信を行い、送信完了後に

『国民経済計算年報』から「国内家計最終消費支出」と「家計国民可処分 所得」の 1970 年〜 1996 年の年次データ (

Dual I/O リードコマンドは、SI/SIO0、SO/SIO1 のピン機能が入出力に切り替わり、アドレス入力 とデータ出力の両方を x2

 筆記試験は与えられた課題に対して、時間 内に回答 しなければなりません。時間内に答 え を出すことは働 くことと 同様です。 だから分からな い問題は後回しでもいいので

また、各メーカへのヒアリングによ って各機器から発生する低周波音 の基礎データ (評価書案 p.272 の表 8.3-33