#include <search.h>
void *tsearch(const void *key, void **rootp,
int (*compar)(const void *, const void *));
void *tfind(const void *key, const void **rootp,
int (*compar)(const void *, const void *));
void *tdelete(const void *key, void **rootp,
int (*compar)(const void *, const void *));
void twalk(const void *root, void (*action)(const void *nodep,
const VISIT which,
const int depth));
#define _GNU_SOURCE
#include <search.h>
void tdestroy(void *root, void (*free_node)(void *nodep));
tsearch() は、木構造からアイテムを検索する関数である。 key は、検索するアイテムへのポインタである。 rootp は木構造の根へのポインタへのポインタである。 木構造がノードを含まない場合、rootp の参照している変数は NULL に設定されていなければならない。 木構造にアイテムが見つかった場合、 tsearch() はそのアイテムへのポインタを返す。 見つからなかった場合は、アイテムを木構造に追加し、 追加したアイテムへのポインタを返す。
tfind() は、 tsearch() に似ているが、 アイテムが見つからなかった場合 NULL を返す点が異なる。
tdelete() は木構造からアイテムを削除する。 引数は tsearch() と同じである。
twalk() は、二分木を深さ優先 (depth-first) で、 左から右にたどっていく関数である。 root は起点となるノードへのポインタである。 root に根以外のノードを指定すると、部分木が対象となる。 twalk() は、ノードを訪れる度に (つまり、内部ノードに対しては 3 回、葉に対しては 1 回) ユーザ関数 action を呼び出す。 action には以下の順に 3 つの引数が与えられる。 最初の引数は訪れたノードへのポインタである。 2 つ目の引数には、内部ノードの場合は訪問回数に応じて preorder, postorder, endorder が、 葉の場合は leaf が与えられる。 (これらのシンボルは <search.h> で定義されている。) 3 つ目の引数はノードの深さで、根の場合は 0 である。
(より一般的には、preorder, postorder, endorder は preorder, inorder, postorder として知られている: それぞれ、子要素を辿る前・最初の子要素を辿った後かつ 2 番目の子要素を辿る前・ 子要素を辿った後ということを表している。 よって postorder という名前を選ぶのは少し紛らわしい。)
tdestroy() は root が指す木構造全体を削除し、 tsearch() 関数で確保されたリソースを全て解放する。 木構造の各ノードについて、関数 free_node が呼び出される。 データへのポインタがこの関数の引数として渡される。 そのような動作が必要でなければ、 free_node は何もしない関数へのポインタでなければならない。
tdelete() は削除したアイテムの親へのポインタを返す。 アイテムが見つからなかった場合は NULL を返す。
rootp が NULL の場合、 tsearch(), tfind(), tdelete() は NULL を返す。
twalk() においては、postorder は 「左の部分木の後で、右の部分木の前」を意味している。 しかし、人によってはこれを "inorder" と呼んで、 "postorder" を「両方の部分木の後」とする場合もある。
tdelete() は、削除したノードの使用していたメモリを解放するが、 ノードに対応するデータのメモリは、ユーザが解放しなければならない。
下のプログラム例は、ユーザ関数が "endorder" か "leaf" を引数にして 呼び出されて以降は、 twalk() がそのノードを参照しないことを前提としている。 これは GNU ライブラリの実装では機能するが、System V のマニュアルには存在しない。
#define _GNU_SOURCE /* Expose declaration of tdestroy() */
#include <search.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
void *root = NULL;
void *
xmalloc(unsigned n)
{
void *p;
p = malloc(n);
if (p)
return p;
fprintf(stderr, "insufficient memory\n");
exit(EXIT_FAILURE);
}
int
compare(const void *pa, const void *pb)
{
if (*(int *) pa < *(int *) pb)
return -1;
if (*(int *) pa > *(int *) pb)
return 1;
return 0;
}
void
action(const void *nodep, const VISIT which, const int depth)
{
int *datap;
switch (which) {
case preorder:
break;
case postorder:
datap = *(int **) nodep;
printf("%6d\n", *datap);
break;
case endorder:
break;
case leaf:
datap = *(int **) nodep;
printf("%6d\n", *datap);
break;
}
}
int
main(void)
{
int i, *ptr;
void *val;
srand(time(NULL));
for (i = 0; i < 12; i++) {
ptr = (int *) xmalloc(sizeof(int));
*ptr = rand() & 0xff;
val = tsearch((void *) ptr, &root, compare);
if (val == NULL)
exit(EXIT_FAILURE);
else if ((*(int **) val) != ptr)
free(ptr);
}
twalk(root, action);
tdestroy(root, free);
exit(EXIT_SUCCESS);
}