2分探索木の全てのパスに対してなんかする。
どうもです。
4回目のコーヒーゼリーをまだ作ってない whitypig です。
いや,4回目を作ろうと思って,ゼラチンを買ってきたのはいいが,
インスタントコーヒーを買ってくるのを忘れた。
さすがにコーヒーメーカーで作ったコーヒーを使う気にはなれず,
また,コンビニに行けばあるけど,高いので渋々あきらめました。
2分木のすべての経路に対してなんかしたい。
全てのノードについてなんかするはいけるけど,根から葉までの各パスについて
なんかしたいというのは,ググっても見つかりませんでした。
だもんで,頑張って書いたんだけど,不安だから,さらして,
間違いを指摘してもらおうというのが,今回の裏テーマ。
あっ,裏テーマ言っちゃった。
ソース
実際に使ったものを若干修正したので動くか不安。
不安なら,確かめよう!
というわけで,必要な部品も用意してきた。
ちなみに,root の親ノードは,NULL というのが前提です。
#include <stdio.h> #include <stdlib.h> #include <time.h> #include <assert.h> enum { NUM = 10, }; typedef struct node { int key; struct node *left; struct node *right; struct node *parent; /* その他のメンバは何でも。 */ } NODE; static void do_something_on_each_path(); static void do_something_on_path(NODE *path[]); static void setup_random_array(int array[], int n); static void setup_tree(NODE **root, int array[], int n); static void setup_random_tree(NODE **root); static void dispose_tree(NODE *root); static int insert(NODE **root, int key); int main(void) { do_something_on_each_path(); return 0; } /** * 全てのパスを調べる。 * ここで言うパスとは,根から葉までの1つの経路のこと。 * 5回,木を作って各パスを表示してみる。 */ static void do_something_on_each_path(void) { /* * 適当にバランスの取れた 2 分木なら, * 10万くらいまでのノードを 40 でカバーできるはず。 * 心配なら,path を動的に確保するリストなり, * スタックなりにすればいいと思います。 * というか,スタックにするべき。 */ NODE *path[40] = { NULL }; /* パスを記録する */ int path_ix = -1; int i; NODE *root = NULL; NODE *p = NULL, *pp = NULL; const int TIMES_OF_TEST = 5; /* テストの回数 */ int times = 0; /* 実行したテストの回数 */ init: if (times >= TIMES_OF_TEST) { return; } for (i = 0; i < 40; i++) { path[i] = NULL; } root = NULL; /* 適当な木を作る */ printf("[%3d]\n", times + 1); setup_random_tree(&root); /* 一番左のパスを記録して,それを基準にする */ path_ix = 0; for (p = root, pp = NULL; p != NULL;) { path[path_ix++] = p; pp = p; if (p->left != NULL) { p = p->left; } else if (p->right != NULL) { /* 左に行けなかったら右に行けるか調べる */ /* 実は,ここの else if が抜けていてバグってたのらぁ。*/ p = p->right; } else { /* 葉まで降りてきた */ break; } } /* まず,このパスに対してしたいことをする */ do_something_on_path(path); /* 一番左のパスを元に調べて行く。 */ for (;;) { for (;;) { /* 前回のパスの途中で,行ってない最下の分岐点まで降りる */ p = path[--path_ix]; pp = p->parent; if (pp == NULL) { /* root に帰ってきた */ dispose_tree(root); /* 木を解放してあげる */ times++; goto init; } if (p == pp->left && pp->right != NULL) { /* その1つ上に右の分岐点があればそちらへ行く */ p = pp->right; break; } } /* p から葉まで,path に記録しながら降りる */ for (;;) { path[path_ix++] = p; if (p->left != NULL) { p = p->left; } else if (p->right != NULL) { p = p->right; } else { /* 葉まで降りてきた */ path[path_ix] = NULL; do_something_on_path(path); break; } } } } /** * パスを表示するだけ。 * path[] は NULL で終端されていると仮定できる。 */ static void do_something_on_path(NODE *path[]) { int i; for (i = 0; path[i] != NULL; i++) { assert(i < 40); printf("%d,", path[i]->key); } printf("\n"); } static void setup_tree(NODE **root, int array[], int n) { int i; for (i = 0; i < n; i++) { assert(insert(root, array[i]) == 0); } } static void setup_random_tree(NODE **root) { int i; int array[NUM] = { -1 }; setup_random_array(array, NUM); printf("[array]: "); for (i = 0; i < NUM; i++) { printf("%d, ", array[i]); } printf("\n"); setup_tree(root, array, NUM); } /** * ランダムで重複なしの値を配列に格納する。 */ static void setup_random_array(int array[], int n) { int i, j, val; char f_created[(NUM * 10 + 7) / (sizeof(char) * 8)] = { 0 }; /* ランダムデータを用意する */ srand(clock() + rand()); for (i = 0; i < n; i++) { for (;;) { val = rand() % (NUM * 10); char c = f_created[val / 8 ]; if ((c & (1U << (val % 8))) == 0) { f_created[val / 8] |= (1U << (val % 8)); array[i] = val; break; } } } /* 重複チェック */ for (i = 0; i < NUM; i++) { for (j = 0; j < NUM; j++) { if (i != j) { assert(array[i] != array[j]); } } } } static void dispose_tree(NODE *p) { if (p == NULL) { return; } else { dispose_tree(p->left); dispose_tree(p->right); free(p); } } int insert(NODE **root, int key) { NODE *p = NULL, *pp = NULL; if (root == NULL) { return -1; } if (*root == NULL) { *root = malloc(sizeof(NODE)); (*root)->key = key; (*root)->parent = NULL; (*root)->left = (*root)->right = NULL; return 0; } p = *root; pp = NULL; while (p) { pp = p; if (key < p->key) { p = p->left; } else if (key > p->key) { p = p->right; } else { /* キーが重複している */ return -1; } } NODE *newnode = malloc(sizeof(NODE)); newnode->left = newnode->right = NULL; newnode->key = key; newnode->parent = pp; if (key < pp->key) { pp->left = newnode; } else { pp->right = newnode; } return 0; }
実行結果
配列の中身と,各パスを表示しています。
配列の 0 番目から順番に木に挿入しています。
2個目のパターンまで紙に書いて,手動で確認したところで
バグを発見して。んで,それを修正したので,たぶん大丈夫なはず。
% ./allpath [ 1] [array]: 19, 27, 79, 75, 28, 17, 25, 13, 57, 59, 19,17,13, 19,27,25, 19,27,79,75,28,57,59, [ 2] [array]: 43, 72, 42, 74, 32, 19, 73, 8, 20, 1, 43,42,32,19,8,1, 43,42,32,19,20, 43,72,74,73, [ 3] [array]: 45, 16, 23, 75, 97, 32, 25, 18, 98, 73, 45,16,23,18, 45,16,23,32,25, 45,75,73, 45,75,97,98, [ 4] [array]: 91, 46, 77, 62, 78, 86, 55, 30, 73, 32, 91,46,30,32, 91,46,77,62,55, 91,46,77,62,73, 91,46,77,78,86, [ 5] [array]: 94, 56, 98, 47, 81, 93, 33, 70, 78, 37, 94,56,47,33,37, 94,56,81,70,78, 94,56,81,93, 94,98,
まとめ
頭がよい人はもっとスマートな方法で見つけられると思うので,
何か知っている人は,参考ページなどを教えてもらえたら,
ワタシは非常にうれしいです。
あと,綺麗ではないソースだとは思いますが,
突っ込んでくれるとうれしいです。