Download presentation
Presentation is loading. Please wait.
1
プログラミング 4 木構造とヒープ
2
木構造 データを入れたノード (node;節点)とノード を結ぶリンク(link;枝) からなる
ノードはリンクによって (複数の)子ノードを持 ち,木のような構造をし ている 木は「根」上にして書く 一番上のノードを根ノード 子のないノードを葉ノード
3
二分木 木構造は,1 つのノードが最大いくつの子ノードを持 てるかによって大別される
二分木,三分木,……,n 分木(3 以上は多分木という) この授業では特に重要な二分木(binary tree)を扱 う 以降,データとして整数を保持する二分木を扱う
4
二分探索木 二分木の中で,ノードの結び方に規則があるもののひ とつ
左の部分木には自分より小さい値,右の部分木には自 分より大きい値を持つノード 下の図では B < A < C A B C
5
二分探索木を作る 31,41,59,26,53,58,97,93 31 41
6
二分探索木を作る 31,41,59,26,53,58,97,93 31 41 59
7
二分探索木を作る 31,41,59,26,53,58,97,93 31 26 41 59
8
二分探索木を作る 31,41,59,26,53,58,97,93 31 26 41 59 53 58
9
二分探索木を作る 31,41,59,26,53,58,97,93 31 26 41 59 53 97 58 93
10
二分木の実現 ノードは構造体で作る struct node { int val; /* 値 */ struct node *left; /* 左子ノード */ struct node *right; /* 右子ノード */ }; 以降, typedef struct node Node; として話を進める 根ノードを指すポインタを root とする
11
二分探索木の探索(1) 大小関係を使って探索する(x を探索する) Node *search(int x) { Node *p = root; while (p != NULL) { if (p->val == x) return p; if (p->val > x) { p = p->left; } else { p = p->right; } } return NULL; }
12
二分探索木の探索(2) ノード数 n の二分探索 木の探索の計算量
木の高さは,二分探索木が うまく作られていれば O(log n) →探索の平均計算量は O(log n) 木がうまく作られていない 場合は木の高さは O(n) →探索の最悪計算量は O(n)
13
二分探索木の作成(1) 同じ値のノードの重複は許さないものとする
探索してノードを新しく作る場所が見つかったら生成 void addnode(int x) { Node *p, *q, *r; p = (Node *)malloc(sizeof(Node)); p->val = x; p->left = NULL; p->right = NULL; if (root == NULL) { root = p; } else { q = root; r = NULL; while (1) { r = q; if (x < r->val) { q = q->left; if (q == NULL) { r->left = p; return; } } else { q = q->right; if (q == NULL) { r->right = p; return; } } } } }
14
二分探索木の生成(2) ノード数 n の二分探索木の生成の計算量
うまく作れていれば木の高さは O(log n) →1 回のノードの追加の計算量は O(log n) これを n 回行うから,平均計算量は O(log n)×O(n) → O(n log n) うまく作れていなければ木の高さは O(n) →1 回のノードの追加の計算量は O(n) これを n 回行うから,最悪計算量は O(n)×O(n) → O(n2)
15
二分探索木をたどる 左の部分木→ノードを出力→右の部分木の順でたどれ ば昇順に出力できる void travtree(Node *p) { if (p == NULL) return; travtree(p->left); printf(“- %d ”, p->val); travtree(p->right); } 再帰呼び出しを使って記述する
16
二分木を解体する 左右の部分木を解体したあと,自身を解放する
順序に注意 void destroytree(Node *p) { if (p == NULL) return; destroytree(p->left); destroytree(p->right); free(p); return; }
17
その他の二分木の処理 ノードを数える 左の部分木のノード数+1+右の部分木のノード数
ノードを数える 左の部分木のノード数+1+右の部分木のノード数 高さを調べる 左の部分木と右の部分木の高さの高いほう+1 葉ノードを数える 右も左も子ノードがないノードの数を数える
18
ヒープ(1) 親ノードがつねに子ノードより大きく(小さく)なら ない二分木をヒープ(heap)という ヒープは配列で実装されることが多い ?
9 8 4 7 2 1 3 6 5 ? 9 8 4 7 3 2 1 6 5
19
ヒープ(2) ノード i について,親ノードは i/2,子ノードは i*2 および i*2+1 ? 9 8 4 7 3 2 1 6 5 9
20
ヒープを作る ヒープの配列を a[] とし,データ数を n とする
新しく x を追加するときは,x をいったん配列の末尾に入 れて,親ノードと比較して大きかったら入れ替える void insnode(int x) { int i, j, t; n++; a[n] = x; i = n; while (i > 1) { j = i / 2; if (a[i] < a[j]) return; t = a[i]; a[i] = a[j]; a[j] = t; i = j; } return; }
21
ヒープから取り出す 根ノードを取り出すと最大値(最小値)になる
末尾のノードを根ノードに移し,ヒープを再構成する int extnode(void) { int i, j, t; a[0] = a[1]; a[1] = a[n]; n--; i = 1; while (i * 2 <= n) { j = i * 2; if (a[j] <= a[j + 1]) j++; if (a[i] < a[j]) return a[0]; t = a[i]; a[i] = a[j]; a[j] = t; i = j; } return a[0]; }
22
ヒープソート ヒープに一度データを全部追加し,順に全部取り出せ ば整列ができる→ヒープソート(heap sort)
ヒープの生成に O(n log n),ヒープからデータを取 り出すのに O(n log n) の計算量 → O(n log n)+O(n log n) → O(n log n)
Similar presentations
© 2024 slidesplayer.net Inc.
All rights reserved.