/**
* 实验题目:
* 求一颗二叉排序树查找成功和失败情况下的平均查找长度
* 实验目的:
* 掌握二叉排序树的查找过程及其算法设计
* 实验内容:
* 设计程序,对于给定的关键字序列,构造一颗二叉排序树bt,
* 并求bt在查找成功和失败情况下的平均查找长度。
*/
#include <stdio.h>
#include <stdbool.h>
#include <malloc.h>
#define MAX_SIZE 100
typedef int key_type; // 定义关键字类型
typedef char info_type;
typedef struct node { // 记录类型
key_type key; // 关键字项
info_type data; // 其他数据域
struct node *lchild; // 左孩子指针
struct node *rchild; // 右孩子指针
}BSTNode;
/*----------------以括号表示法输出二叉排序树bt------------------*/
void disp_bst(BSTNode *bt); // 函数声明
bool insert_bst(BSTNode *&bt, key_type key) // 在以bt为根结点的BST中插入一个关键字为key的结点
{
if(bt == NULL) // 原树为空,新插入的记录为根结点
{
bt = (BSTNode *)malloc(sizeof(BSTNode));
bt->key = key;
bt->lchild = bt->rchild = NULL;
return true;
}
else if(key == bt->key)
return false;
else if(key < bt->key)
return insert_bst(bt->lchild, key); // 插入到bt结点的左子树中
else
return insert_bst(bt->rchild, key); // 插入到bt结点的右子树中
}
/*----------------由数组a中的关键字建立一颗二叉排序树------------------*/
BSTNode *create_bst(key_type a[], int n)
{
BSTNode *bt = NULL; // 初始时bt为空树
int i = 0;
while(i < n)
{
if(insert_bst(bt, a[i]) == 1) // 将a[i]插入二叉排序树bst中
{
printf(" 第%d步,插入%d:", i + 1, a[i]);
disp_bst(bt);
printf("\n");
i++;
}
}
return bt;
}
/*----------------以括号表示法输出二叉排序树bt------------------*/
void disp_bst(BSTNode *bt)
{
if(bt != NULL)
{
printf("%d", bt->key);
if(bt->lchild != NULL || bt->rchild != NULL)
{
printf("("); // 有孩子结点时才输出(
disp_bst(bt->lchild); // 递归处理左子树
if(bt->rchild != NULL)
printf(","); // 有右孩子结点时才输出,
disp_bst(bt->rchild); // 递归处理右子树
printf(")"); // 有孩子结点时才输出)
}
}
}
key_type predt = -32767; // predt为全局变量,保存当前结点中序前驱的值,初值为-∞
/*----------------判断bt是否为BST------------------*/
bool judge_bst(BSTNode *bt)
{
bool b1, b2;
if(bt == NULL)
return true;
else
{
b1 = judge_bst(bt->lchild);
if(b1 == false || predt >= bt->key)
return false;
b2 = judge_bst(bt->rchild);
return b2;
}
}
/*----------------销毁一颗BST------------------*/
void destroy_bst(BSTNode *bt)
{
if(bt != NULL)
{
destroy_bst(bt->lchild);
destroy_bst(bt->rchild);
free(bt);
}
}
/*----------------以非递归方式输出从根结点到查找到的结点的路径------------------*/
void search_bst1(BSTNode *bt, key_type key, key_type path[], int i)
{
int j;
if(bt == NULL)
return;
else if(key == bt->key) // 找到关键字为key的结点
{
path[i + 1] = bt->key; // 输出其路径
for(j = 0; j <= i + 1; j++)
{
printf("%3d", path[j]);
}
printf("\n");
}
else
{
path[i + 1] = bt->key;
if(key < bt->key)
search_bst1(bt->lchild, key, path, i + 1); // 在左子树中递归查找
else
search_bst1(bt->rchild, key, path, i + 1); // 在右子树中递归查找
}
}
/*----------------以递归方式输出从根结点到查找到的结点的路径------------------*/
int search_bst2(BSTNode *bt, key_type key)
{
if(bt == NULL)
return 0;
else if(key == bt->key) // 找到关键字为key的结点
{
printf("%3d", bt->key); // 输出关键字
return 1;
}
else if(key < bt->key)
{
search_bst2(bt->lchild, key); // 在左子树中递归查找
}
else
{
search_bst2(bt->rchild, key); // 在右子树中递归查找
}
printf("%3d", bt->key); // 输出关键字
}
/*----------------被删除的结点p有左、右子树,r指向其左孩子------------------*/
void delete_node1(BSTNode *p, BSTNode *&r) // 指针的引用
{
BSTNode *q;
if(r->rchild != NULL) // 递归找结点r的最右下结点
{
delete_node1(p, r->rchild);
}
else // 找到最右下结点r(它没有右子树)
{
p->key = r->key; // 将结点r的值存放到结点p中(结点值替代)
p->data = r->data;
q = r; // 删除结点r
r = r->lchild; // 即用结点r的左孩子替代它
free(q); // 释放结点r的空间
}
}
/*----------------从二叉排序树中删除p结点------------------*/
void delete_node(BSTNode *&p)
{
BSTNode *q;
if(p->rchild == NULL) // p结点没有右子树的情况
{
q = p;
p = p->lchild;
free(q);
}
else if(p->lchild == NULL) // p结点没有左子树的情况
{
q = p;
p = p->rchild;
free(q);
}
else
delete_node1(p, p->lchild); // p结点既有左子树又有右子树的情况
}
/*----------------在bt中删除关键字为key的结点------------------*/
bool delete_bst(BSTNode *&bt, key_type key)
{
if(bt == NULL) // 空树删除失败
return false;
else
{
if(key < bt->key)
return delete_bst(bt->lchild, key); // 递归在左子树中删除关键字为key的结点
else if(key > bt->key)
return delete_bst(bt->rchild, key); // 递归在右子树中删除关键字为key的结点
else // key = bt->key的情况
{
delete_node(bt); // 调用函数delete_node删除bt结点
return true;
}
}
}
/*--------------求查找成功总的比较次数sum_len和情况数m-------------*/
static void succ_length(BSTNode *bt, int &sum_len, int &m, int level) // 引用类型
{
if(bt == NULL)
return;
m++;
sum_len += level;
succ_length(bt->lchild, sum_len, m, level + 1);
succ_length(bt->rchild, sum_len, m, level + 1);
}
/*--------------求查找成功情况下的平均查找长度-------------*/
static double asl_succ(BSTNode *bt)
{
int sum_len = 0;
int m = 0;
succ_length(bt, sum_len, m, 1);
return sum_len * 1.0 / m;
}
/*--------------求查找失败总的比较次数sum_len和情况数m-------------*/
static void unsucc_length(BSTNode *bt, int &sum_len, int &m, int level) // 引用类型
{
if(bt == NULL) // 空指针对应外部结点
{
m++;
sum_len += level - 1;
return;
}
unsucc_length(bt->lchild, sum_len, m, level + 1);
unsucc_length(bt->rchild, sum_len, m, level + 1);
}
/*--------------求查找失败情况下的平均查找长度-------------*/
static double asl_unsucc(BSTNode *bt)
{
int sum_len = 0;
int m = 0;
unsucc_length(bt, sum_len, m, 1);
return sum_len * 1.0 / m;
}
int main(int argc, char *argv[])
{
BSTNode *bt;
int n = 12; // 关键字序列长度
key_type a[] = {25, 18, 46, 2, 53, 39, 32, 4, 74, 67, 60, 11}; // 关键字序列
printf("(1)创建BST\n");
bt = create_bst(a, n);
printf("(2)BST:");
disp_bst(bt);
printf("\n");
printf("(3)查找成功情况下的平均查找长度 = %g\n", asl_succ(bt));
printf("(4)查找失败情况下的平均查找长度 = %g\n", asl_unsucc(bt));
destroy_bst(bt); // 销毁二叉排序树
return 0;
}
测试结果:
(1)创建BST
第1步,插入25:25
第2步,插入18:25(18)
第3步,插入46:25(18,46)
第4步,插入2:25(18(2),46)
第5步,插入53:25(18(2),46(,53))
第6步,插入39:25(18(2),46(39,53))
第7步,插入32:25(18(2),46(39(32),53))
第8步,插入4:25(18(2(,4)),46(39(32),53))
第9步,插入74:25(18(2(,4)),46(39(32),53(,74)))
第10步,插入67:25(18(2(,4)),46(39(32),53(,74(67))))
第11步,插入60:25(18(2(,4)),46(39(32),53(,74(67(60)))))
第12步,插入11:25(18(2(,4(,11))),46(39(32),53(,74(67(60)))))
(2)BST:25(18(2(,4(,11))),46(39(32),53(,74(67(60)))))
(3)查找成功情况下的平均查找长度 = 3.5
(4)查找失败情况下的平均查找长度 = 4.15385