1、二叉查找树(也叫二叉搜索树,或二叉排序树)或者是一棵空树,或者是具有下列性质的二叉树:
1)若它的左子树不为空,则左子树上所有结点的值均小于它的根结点的值;
2)若它的右子树不为空,则右子树上所有结点的值均大于它的根结点的值;
3)它的左、右子树也分别为二叉查找树。
2、二叉查找树的基本运算
- #include <stdio.h>
- #include <stdlib.h>
- typedef int data_type;
- typedef struct bst_node {
- data_type data;
- struct bst_node *lchild, *rchild;
- }bst_t, *bst_p;
在二叉查找树中插入新结点,要保证插入新结点后仍能满足二叉查找树的性质。例子中的插入过程如下:
a、若二叉查找树root为空,则使新结点为根;
b、若二叉查找树root不为空,则通过search_bst_for_insert函数寻找插入点并返回它的地址(若新结点中的关键字已经存在,则返回空指针);
c、若新结点的关键字小于插入点的关键字,则将新结点插入到插入点的左子树中,大于则插入到插入点的右子树中。
- static bst_p search_bst_for_insert(bst_p *root, data_type key)
- {
- bst_p s, p = *root;
- while (p) {
- s = p;
- if (p->data == key)
- return NULL;
- p = (key < p->data) ? p->lchild : p->rchild;
- }
- return s;
- }
- void insert_bst_node(bst_p *root, data_type data)
- {
- bst_p s, p;
- s = malloc(sizeof(struct bst_node));
- if (!s)
- perror("Allocate dynamic memory");
- s -> data = data;
- s -> lchild = s -> rchild = NULL;
- if (*root == NULL)
- *root = s;
- else {
- p = search_bst_for_insert(root, data);
- if (p == NULL) {
- fprintf(stderr, "The %d already exists.\n", data);
- free(s);
- return;
- }
- if (data < p->data)
- p->lchild = s;
- else
- p->rchild = s;
- }
- }
- static int print(data_type data)
- {
- printf("%d ", data);
- return 1;
- }
- int pre_order_traverse(bst_p root, int (*visit)(data_type data))
- {
- if (root) {
- if (visit(root->data))
- if (pre_order_traverse(root->lchild, visit))
- if (pre_order_traverse(root->rchild, visit))
- return 1;
- return 0;
- }
- else
- return 1;
- }
- int post_order_traverse(bst_p root, int (*visit)(data_type data))
- {
- if (root) {
- if (post_order_traverse(root->lchild, visit))
- if (visit(root->data))
- if (post_order_traverse(root->rchild, visit))
- return 1;
- return 0;
- }
- else
- return 1;
- }
中序遍历二叉查找树可得到一个关键字的有序序列。
3)删除
删除某个结点后依然要保持二叉查找树的特性。例子中的删除过程如下:
a、若删除点是叶子结点,则设置其双亲结点的指针为空。
b、若删除点只有左子树,或只有右子树,则设置其双亲结点的指针指向左子树或右子树。
c、若删除点的左右子树均不为空,则:
1)、查询删除点的右子树的左子树是否为空,若为空,则把删除点的左子树设为删除点的右子树的左子树。
2)、若不为空,则继续查询左子树,直到找到最底层的左子树为止。
- void delete_bst_node(bst_p *root, data_type data)
- {
- bst_p p = *root, parent, s;
- if (!p) {
- fprintf(stderr, "Not found %d.\n", data);
- return;
- }
- if (p->data == data) {
- /* It's a leaf node */
- if (!p->rchild && !p->lchild) {
- *root = NULL;
- free(p);
- }
- /* the right child is NULL */
- else if (!p->rchild) {
- *root = p->lchild;
- free(p);
- }
- /* the left child is NULL */
- else if (!p->lchild) {
- *root = p->rchild;
- free(p);
- }
- /* the node has both children */
- else {
- s = p->rchild;
- /* the s without left child */
- if (!s->lchild)
- s->lchild = p->lchild;
- /* the s have left child */
- else {
- /* find the smallest node in the left subtree of s */
- while (s->lchild) {
- /* record the parent node of s */
- parent = s;
- s = s->lchild;
- }
- parent->lchild = s->rchild;
- s->lchild = p->lchild;
- s->rchild = p->rchild;
- }
- *root = s;
- free(p);
- }
- }
- else if (data > p->data) {
- delete_bst_node(&(p->rchild), data);
- }
- else if (data < p->data) {
- delete_bst_node(&(p->lchild), data);
- }
- }
同样的关键字,以不同的插入顺序,会产生不同形态的二叉查找树。
- int main(int argc, char *argv[])
- {
- int i, num;
- bst_p root = NULL;
- if (argc < 2) {
- fprintf(stderr, "Usage: %s num\n", argv[0]);
- exit(-1);
- }
- num = atoi(argv[1]);
- data_type arr[num];
- printf("Please enter %d integers:\n", num);
- for (i = 0; i < num; i++) {
- scanf("%d", &arr[i]);
- insert_bst_node(&root, arr[i]);
- }
- printf("\npre order traverse: ");
- pre_order_traverse(root, print);
- printf("\npost order traverse: ");
- post_order_traverse(root, print);
- printf("\n");
- delete_bst_node(&root, 45);
- printf("\npre order traverse: ");
- pre_order_traverse(root, print);
- printf("\npost order traverse: ");
- post_order_traverse(root, print);
- printf("\n");
- return 0;
- }