2分探索木Ⅰ(pdfファイル)

2分探 索木Ⅰ
2分探索木Ⅰ
0.目次
1.2分探索木とは
2.2分探索木の作成
3.2分探索木の走査
3.1
3.2
3.3
前走査
中走査
問題
問題1
問題2
後走査
4.2分探索木の表示
- 1 -
2分探 索木Ⅰ
1.2分探索木とは
木 はいくつかの節点と節点同士を結ぶ辺から構成される。
2 つ の 節 点 u,vが 直 接 辺 で 結 ば れ て い る と き 、 一 方 を 親 節 点 、 他 方 を 子 節 点 と い
う。ある節点の親節点は高々1つであり、子節点は0個以上である。節点の中に
は、親節点をもたない節点がただ1つあり、根という。また、子節点をもたない
節点を葉という。木は、つぎのように定義される。
(1)ひとつの節点は、木である。
( 2 ) vが 節 点 で 、 T 1 ,T 2 ,・・・,T k が 木 で 、 そ れ ぞ れ の 木 の 根 が v 1 ,v 2 ,・・・,v k
と す る 。 こ の と き 、 vを v 1 ,v 2 ,・・・,v k の 親 と す る の は 、 木 で あ る 。
節 点 v 1 ,v 2 ,・・・,v k は 節 点 vの 子 と 呼 ば れ る 。
v
v1
v2
・・・
T1
vk
Tk
T2
2 分 木は、どの節点も高々2個の子節点をもつ。2個の子節点の内、左に
書かれる子節点を根とする部分木を左部分木、右に書かれる子節点を根とする
部分木を右部分木という。
A
B
D
C
E
F
G
H
節点:A,B,C,D,E,F,G,H
(Aは根でもある)
辺:AB,AC,BD,BE,CF,FG,FH
(XYのとき、Xが親、Yが子を意味する)
2分木の節点に集合Sの要素を割り当てる。各節点において、割り当てられた
要素の大きさが、左部分木のどの節点の要素よりも大きく、右部分木のどの節点
の要素よりも小さいとき、2 分 探 索 木 という。
- 2 -
2分探 索木Ⅰ
2.2分探索木の作成
空の2分探索木から始める。挿入する要素と節点の要素を比較し、前者が小さ
い場合、左部分木に追加し、前者が大きい場合、右部分木に追加していく。
●手順
9個 の デ ー タ (55,33,11,99,44,77,22,66,88)で 手 順 を 示 す 。
(1)
(2)
55
(3)
55
55
33
33
11
(4)
(5)
55
33
99
55
33
11
99
11
(6)
(7)
55
33
11
44
55
99
44
33
77
99
11
44
77
22
(8)
(9)
55
33
11
55
99
44
22
33
77
11
66
99
44
22
77
66
88
2 分 探 索 木 を 表 現 す る た め に 、 デ ー タ を 保 存 す る 変 数 info、 左 部 分 木 を 指 す ポ
イ ン タ left、 右 部 分 木 を 指 す ポ イ ン タ rightを も つ 構 造 体 を 用 意 す る 。 根 を 指 す
ポ イ ン タ 変 数 を rootと す る 。 ポ イ ン タ 変 数 rootは 空 の 同 じ 構 造 体 を 指 す 。
このようにすると、プログラムが簡潔になる。
root
B
NULL
A
D
B
C
E
NULL
A
NULL
- 3 -
D
NULL
C
NULL
NULL
E
NULL
2分探 索木Ⅰ
● プ ロ グ ラ ム ・ 非 再 帰 版 ( bt211.c)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
/* << bt211.c >> */
/* 2 分 探 索 木 の 作 成 。 */
#include <stdio.h>
#include <stdlib.h>
struct NODE {
int info;
/* 節 点 が も つ デ ー タ 。 */
struct NODE *left; /* 左 の 子 節 点 を 指 す ポ イ ン タ 。 */
struct NODE *right; /* 右 の 子 節 点 を 指 す ポ イ ン タ 。 */
};
struct NODE *mktree();
int main() {
struct NODE *root;
/* 2 分 探 索 木 の 作 成 。 */
root = mktree();
}
/* 2 分 探 索 木 の 作 成 。 */
struct NODE *mktree() {
int data;
struct NODE *p,
/*
*q,
/*
*r,
/*
*root; /*
現 節 点 を 指 す ポ イ ン タ 変 数 。 */
現 節 点 の 親 節 点 を 指 す ポ イ ン タ 変 数 。 */
作 業 用 ポ イ ン タ 変 数 。 */
根 を 指 す ポ イ ン タ 変 数 。 */
/* 根 を 指 す ポ イ ン タ 変 数 と 空 の 構 造 体 の 作 成 。 */
root = (struct NODE *)malloc(sizeof(struct NODE));
root->left = NULL;
root->right = NULL;
while( 1 ) {
/* デ ー タ の 読 み 込 み 。 */
scanf("%d",&data);
if( data <= 0 ) { break; }
r = (struct NODE *)malloc(sizeof(struct NODE));
r->info = data;
r->left = NULL;
r->right = NULL;
root->info = data; /* 2 分 探 索 木 に デ ー タ を 追 加 す る と き 、
空の構造体の左部分木に追加できるように
root->info=data と し て お く 。 */
/* 初 期 設 定 。 */
p = root->left;
q = root;
/* 追 加 す る 場 所 を 探 す 。 */
while( p != NULL ) {
/* 重 複 デ ー タ は 追 加 し な い 。 */
if( data == p->info ) { goto next; }
- 4 -
2分探 索木Ⅰ
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
/* デ ー タ dataが 現 在 の 節 点 の デ ー タ (p->info)以 下 の と き
左 部 分 木 へ 、 大 き い と き 右 部 分 木 へ 移 動 す る 。 */
q = p;
if( data <= p->info ) {
p = p->left;
} else {
p = p->right;
}
}
/* デ ー タ の 追 加 。 */
if( data <= q->info ) {
/* 左 部 分 木 と し て 追 加 。 */
q->left = r;
} else {
/* 右 部 分 木 と し て 追 加 。 */
q->right = r;
}
next:;
}
return root;
}
- 5 -
2分探 索木Ⅰ
3.2分探索木の走査
2分探索木の辺を反時計回りに(赤い線に沿って)なぞっていくと2分探索木
のすべての節点を訪問できる。
A
B
D
C
E
F
G
H
2分探索木のすべての節点をたどる方法として、前走査、中走査、後走査の
3通り考えられる。
●前走査:まず、親節点を訪問する。
つぎに、左部分木のすべての節点を訪問する。
最後に、右部分木のすべての節点を訪問する。
①A
②B
③D
⑤C
④E
⑥F
⑦G
訪問順:ABDECFGH
- 6 -
⑧H
2分探 索木Ⅰ
●中走査:まず、左部分木のすべての節点を訪問する。
つぎに、親節点を訪問する。
最後に、右部分木のすべての節点を訪問する。
④A
②B
①D
⑤C
③E
⑦F
⑥G
⑧H
訪問順:DBEACGFH
●後走査:まず、左部分木のすべての節点を訪問する。
つぎに、右部分木のすべての節点を訪問する。
最後に、親節点を訪問する。
⑧A
③B
①D
⑦C
②E
⑥F
④G
訪問順:DEBGHFCA
- 7 -
⑤H
2分探 索木Ⅰ
3.1
前走査
● プ ロ グ ラ ム ・ 再 帰 版 (bt311.c)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/* << bt311.c >> */
/* 2 分 探 索 木 の 走 査 ( 前 走 査 )。 */
#include <stdio.h>
#include <stdlib.h>
struct NODE {
int info;
/* 節 点 が も つ デ ー タ 。 */
struct NODE *left; /* 左 の 子 節 点 を 指 す ポ イ ン タ 。 */
struct NODE *right; /* 右 の 子 節 点 を 指 す ポ イ ン タ 。 */
};
void preorder(struct NODE *p);
struct NODE *mktree();
int main() {
struct NODE *root;
/* 2 分 探 索 木 の 作 成 。 */
root = mktree();
/* 前 走 査 。 */
preorder(root->left); printf("\n");
}
/* 前 走 査 。 */
void preorder(struct NODE *p) {
if( p == NULL ) { return; }
printf(" %d",p->info);
preorder(p->left);
preorder(p->right);
}
- 8 -
2分探 索木Ⅰ
実行結果
% cc bt311.c
% ./a.out
55 22 88 11 33 77 99 44 66 0
55 22 11 33 44 88 77 66 99
① 55
② 22
③ 11
⑥ 88
④ 33
⑦ 77
⑤ 44 ⑧ 66
①から⑨の順に訪問する。
% ./a.out
11 22 33 44 55 66 77 88 99 0
11 22 33 44 55 66 77 88 99
% ./a.out
99 88 77 66 55 44 33 22 11 0
99 88 77 66 55 44 33 22 11
% ./a.out
22 11 55 44 33 99 88 77 66 0
22 11 55 44 33 99 88 77 66
- 9 -
⑨ 99
2分探 索木Ⅰ
3.2
中走査
● プ ロ グ ラ ム ・ 再 帰 版 ( bt321.c)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/* << bt321.c >> */
/* 2 分 探 索 木 の 走 査 ( 中 走 査 )。 */
#include <stdio.h>
#include <stdlib.h>
struct NODE {
int info;
/* 節 点 が も つ デ ー タ 。 */
struct NODE *left; /* 左 の 子 節 点 を 指 す ポ イ ン タ 。 */
struct NODE *right; /* 右 の 子 節 点 を 指 す ポ イ ン タ 。 */
};
void inorder(struct NODE *p);
struct NODE *mktree();
int main() {
struct NODE *root;
/* 2 分 探 索 木 の 作 成 。 */
root = mktree();
/* 中 走 査 。 */
inorder(root->left); printf("\n");
}
/* 中 走 査 。 */
void inorder(struct NODE *p) {
if( p == NULL ) { return; }
inorder(p->left);
printf(" %d",p->info);
inorder(p->right);
}
- 10 -
2分探 索木Ⅰ
実行結果
% cc bt321.c
% ./a.out
55 22 88 11 33 77 99 44 66 0
11 22 33 44 55 66 77 88 99
⑤ 55
② 22
① 11
⑧ 88
③ 33
⑦ 77
④ 44 ⑥ 66
①から⑨の順に訪問する。
% ./a.out
11 22 33 44 55 66 77 88 99 0
11 22 33 44 55 66 77 88 99
% ./a.out
99 88 77 66 55 44 33 22 11 0
11 22 33 44 55 66 77 88 99
% ./a.out
22 11 55 44 33 99 88 77 66 0
11 22 33 44 55 66 77 88 99
- 11 -
⑨ 99
2分探 索木Ⅰ
3.3
問題1
問題
後走査
● プ ロ グ ラ ム ・ 再 帰 版 ( bt331.c)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/* << bt331.c >> */
/* 2 分 探 索 木 の 走 査 ( 後 走 査 )。 */
#include <stdio.h>
#include <stdlib.h>
struct NODE {
int info;
/* 節 点 が も つ デ ー タ 。 */
struct NODE *left; /* 左 の 子 節 点 を 指 す ポ イ ン タ 。 */
struct NODE *right; /* 右 の 子 節 点 を 指 す ポ イ ン タ 。 */
};
void postorder(struct NODE *p);
struct NODE *mktree();
int main() {
struct NODE *root;
/* 2 分 探 索 木 の 作 成 。 */
root = mktree();
/* 後 走 査 。 */
postorder(①
); printf("\n");
}
/* 後 走 査 。 */
void postorder(struct NODE *p) {
if( p == ②
) { return; }
postorder(③
);
postorder(④
);
printf(" %d",p->info);
}
- 12 -
2分探 索木Ⅰ
実行結果
% cc bt331.c
% ./a.out
55 22 88 11 33 77 99 44 66 0
11 44 33 22 66 77 99 88 55
⑨ 55
22 ④
① 11
⑧ 88
③ 33
⑥ 77
② 44 ⑤ 66
①から⑨の順に訪問する。
% ./a.out
11 22 33 44 55 66 77 88 99 0
99 88 77 66 55 44 33 22 11
% ./a.out
99 88 77 66 55 44 33 22 11 0
11 22 33 44 55 66 77 88 99
% ./a.out
22 11 55 44 33 99 88 77 66 0
11 33 44 66 77 88 99 55 22
- 13 -
⑦ 99
2分探 索木Ⅰ
問題2
読み込んだデータから2分探索木を構成し、前走査、中走査、後走査でたどる
順を示せ。
例
入 力 デ ー タ : 55 22 88 11 33 77 99 44 66
前 走 査 : 55 22 11 33 44 88 77 66 99
中 走 査 : 11 22 33 44 55 66 77 88 99
後 走 査 : 11 44 33 22 66 77 99 88 55
( 1 ) 入 力 デ ー タ : 44 11 33 77 66 55 99 88 22
前走査
⑤
中走査
⑥
後走査
⑦
- 14 -
2分探 索木Ⅰ
4.2分探索木の表示
2 分 探 索 木 を Excelの グ ラ フ 機 能 を 使 っ て 表 示 す る 。
まず、各節点に座標を割り当てる。節点が交わらないように下図のように座標
を 決 め る 。 根 の 座 標 は (0,0)と す る 。
● (0,0)
● (-1/2,-1)
● (-3/4,-2)
● (1/2,-1)
● (-1/4,-2)
● (1/4,-2)
● (3/4,-2)
す な わ ち 、2 分 探 索 木 の 深 さ が 一 つ 増 え る ご と に 、節 点 間 の 幅 を 1/2倍 に す る 。
こ の プ ロ グ ラ ム で 、 節 点 の 座 標 を 求 め る 。 た だ し 、 Excelの グ ラ フ 機 能 ( 散 布
図)を使って表示するため、前走査で節点を通過するたびに座標を出力する。
● プ ロ グ ラ ム ・ 再 帰 版 ( bt411.c)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/* << bt411.c >> */
/* 2 分 探 索 木 の 走 査 ( 前 走 査 )。 */
/* 節 点 の 座 標 を 出 力 す る 。 */
#include <stdio.h>
#include <stdlib.h>
struct NODE {
int info;
/* 節 点 が も つ デ ー タ 。 */
struct NODE *left; /* 左 の 子 節 点 を 指 す ポ イ ン タ 。 */
struct NODE *right; /* 右 の 子 節 点 を 指 す ポ イ ン タ 。 */
};
float width; /* 幅 。 */
void preorder(struct NODE *p, float v, float h);
struct NODE *mktree();
int main() {
struct NODE *root;
/* 2 分 探 索 木 の 作 成 。 */
root = mktree();
/* 初 期 設 定 。 */
width = 1;
/* 前 走 査 。 */
preorder(root->left,0,0);
printf("\n");
- 15 -
2分探 索木Ⅰ
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
}
/* 前 走 査 に 従 っ て 、 節 点 の x座 標 v,y座 標 hを 出 力 す る 。 */
void preorder(struct NODE *p, float v, float h) {
if( p == NULL ) { return; }
/* 座 標 (v,-h)の 出 力 。 */
printf("%8.5f,%8.5f\n",v,-h);
width = width/2;
preorder(p->left,v-width,h+1);
/* 座 標 (v,-h)の 出 力 。 */
printf("%8.5f,%8.5f\n",v,-h);
preorder(p->right,v+width,h+1);
width = width*2;
/* 座 標 (v,-h)の 出 力 。 */
printf("%8.5f,%8.5f\n",v,-h);
}
実行結果
% cc bt411.c
% ./a.out
44 22 11 33 66 55 77 0
0.00000,-0.00000
-0.50000,-1.00000
-0.75000,-2.00000
-0.75000,-2.00000
-0.75000,-2.00000
-0.50000,-1.00000
-0.25000,-2.00000
-0.25000,-2.00000
-0.25000,-2.00000
-0.50000,-1.00000
0.00000,-0.00000
0.50000,-1.00000
0.25000,-2.00000
0.25000,-2.00000
0.25000,-2.00000
0.50000,-1.00000
0.75000,-2.00000
0.75000,-2.00000
0.75000,-2.00000
0.50000,-1.00000
0.00000,-0.00000
- 16 -
2分探 索木Ⅰ
●2分探索木の表示
① 節 点 の 座 標 を csvフ ァ イ ル ( 拡 張 子 を csvと す る ) に し て 保 存 す る 。
② Excelを 起 動 し 、 csvフ ァ イ ル を 読 み 込 む 。
③グラフ機能(散布図)使ってグラフを表示する。
余分な座標軸等は削除する。
x座標
0
-0.5
-0.75
-0.75
-0.75
-0.5
-0.25
-0.25
-0.25
-0.5
0
0.5
0.25
0.25
0.25
0.5
0.75
0.75
0.75
0.5
0
y座標
0
-1
-2
-2
-2
-1
-2
-2
-2
-1
0
-1
-2
-2
-2
-1
-2
-2
-2
-1
0
44
22
66
11
33
- 17 -
55
77