二叉查找树又称二叉排序树,所有节点均满足左子节点键值小于其根节点;右子节点的键值大于其根节点;不存在两节点关键字相同的二叉树称为二叉查找树。空树也是二叉查找树。
由相同的节点生成的二叉查找树不唯一,最好的情况是生成平衡二叉树而最坏的情况是生成单支二叉树(实际上是线性链表)。在随机的情况下,二叉查找树的平均查找长度为O(log n),若想建立可以实际应用的二叉查找树必须引入自平衡算法。
二叉查找树的搜索
由于二叉查找树节点关键字的大小关系,搜索很自然地可以采用折半查找的策略。这里采用左子树优先的搜索策略,即先搜索左节点再搜索右节点。
node *at(node *root, key_t key_argu) {
node *ptr = root;
while (ptr->key != key_argu) {
if (ptr->left != NULL && key_argu < ptr->key ) {
ptr = ptr->left;
}
else if (ptr->right != NULL && ptr->key < key_argu ) {
ptr = ptr->right;
}
else {
return NULL;
}
}
return ptr;
}
二叉查找树添加节点
上文已提及二叉查找树具有不唯一性,生成的二叉查找树 与添加算法以及添加的顺序有关。这里采用左子树优先的策略添加节点,即在搜索添加位置时先搜索左子树。
由于目标节点不存在而导致左子树优先搜索失败的过程中所搜索路径最后一个节点即为新节点的父节点,比较新节点与父节点的键值可以知道添加到左还是右子节点。
定义node *append_node(node *root, key_t key_argu)
函数实现向指定节点添加指定键值的子节点。当树为空树时只需对根节点赋值,无需建立新节点。
node *append(node *root, key_t key_argu) { // left is prior
node *ptr = root, *nptr;
//empty
if (root->left == NULL && root->right==NULL) {
root->key = key_argu;
return root;
}
//non-empty
//seek position
while (ptr->key != key_argu) {
if (key_argu < ptr->key ) {
if (ptr->left) {
ptr = ptr->left;
}
else {
break;
}
}
else if ( ptr->key < key_argu ) {
if (ptr->right) {
ptr = ptr->right;
}
else {
break;
}
}
}
nptr=append_node(ptr,key_argu);
return nptr;
}
二叉查找树删除节点
删除节点是这里要介绍的最的算法(没有之一),删除节点要保证删除后的数据结构仍然是二叉查找树。因为我们定义根节点作为二叉树的访问接口故不考虑删除根节点的问题。
删除节点要将其父节点指向目标节点的指针以及目标节点的左右子节点重新设定,并释放目标节点的内存空间。若目标节点为叶节点则直接删除 并将父节点指向目标节点的指针置为空指针即可;若目标节点只有一棵子树(只有左子树或只有右子树)则只需要用其子树替代目标节点的位置。
对于具有两棵子树的目标节点,要求将其删除后原左子树接在其原来的位置上;原右子树沿左子树最右侧分支寻找第一个空节点,并接在该空节点上。
定义rm函数作为删除节点操作的接口,定义工具函数rm0来处理目标节点具有两棵子树的情况。
node *rm0(node *root,node *ptr) {//no-root and with a full sub tree
node *temp;
temp = ptr;
// replace ptr for ptr->left sub tree
if (ptr == ptr->prior->left) {
ptr->prior->left = ptr->left;
}
else if (ptr == ptr->prior->right) {
ptr->prior->right = ptr->left;
}
//seek position for ptr->right sub tree
ptr = ptr->left; //update ptr
while (ptr->right) {
ptr = ptr->right;
}
ptr->right = temp->right;
free(temp);
return root;
}
node *rm(node *root,key_t key_argu) { //cannot
node *ptr;
ptr = at(root,key_argu);
if (ptr->left == NULL && ptr->right == NULL) {
if (ptr == ptr->prior->left) {
ptr->prior->left = NULL;
}
else {
ptr->prior->right = NULL;
}
free(ptr);
}
else if (ptr->left != NULL && ptr->right == NULL) {
if (ptr == ptr->prior->left) {
ptr->prior->left = ptr->left;
}
else {
ptr->prior->right = ptr->left;
}
free(ptr);
}
else if (ptr->left == NULL && ptr->right != NULL) {
if (ptr == ptr->prior->left) {
ptr->prior->left = ptr->right;
}
else {
ptr->prior->right = ptr->right;
}
free(ptr);
}
else if (ptr->left ==! NULL && ptr->right != NULL) {
rm0(root,ptr);
}
return root;
}
清空树
清空采用递归的方式进行,类似于中序遍历。
int clear(node *root) {
node *ptr=root,*temp;
if (ptr) {
temp = ptr->right;
clear(ptr->left);
free(ptr);
clear(temp);
}
}
删除某一节点后会使其子树与根节点断开导致无法继续遍历,中序遍历的方式便于在清空后继续访问。程序流程与非递归中序遍历类似,在弹栈并更新当前节点后要将原当前节点删除,这需要一个临时指针存储原当前节点位置。
非递归实现:
int clear2(node *root) {
node *ptr=root,*temp;
node *stack[1024]={NULL};
int top=0;//top of stack
while (ptr || top>0) {
if (ptr) {
stack[top++]=ptr;//push
ptr=ptr->left;
}
else {
ptr=stack[--top];//ptr=stack top;pop;
temp = ptr;
ptr=ptr->right;
free(temp);
}
}
return num;
}
// 完整程序源码
#include <stdio.h>
#include <stdlib.h>
#define key_t char
typedef struct node{
key_t key;
struct node *prior;
struct node *left;
struct node *right;
}node;
int num=0;
inline int init_node(node *this){
this->key='\0';
this->prior=NULL;
this->left=NULL;
this->right=NULL;
return 0;
}
inline node *append_node(node *prior_argu,key_t key_argu) {
node *nptr;
nptr=(node *)malloc(sizeof(node));
init_node(nptr);
nptr->prior = prior_argu;
nptr->key = key_argu;
if (key_argu < prior_argu->key) {
prior_argu->left=nptr;
}
else if (key_argu > prior_argu->key) {
prior_argu->right=nptr;
}
return nptr;
};
//using root ptr,and the root saves data too. using nullptr as the end.
inline int examp_tree(node *root) {
root->key='m';
//1st sub tree
append_node(root,'f');
append_node(root,'s');
//2nd sub tree
append_node(root->left,'d');
append_node(root->left,'i');
append_node(root->right,'p');
append_node(root->right,'v');
//3rd sub tree
append_node(root->left->left,'b');
append_node(root->left->left,'e');
append_node(root->left->right,'g');
append_node(root->left->right,'k');
append_node(root->right->left,'n');
append_node(root->right->left,'q');
append_node(root->right->right,'t');
append_node(root->right->right,'w');
return 14;
}
int traver(node *root) { //unrecursion inorder traver
node *ptr=root;
node *stack[1024]={NULL};
int top=0;//top of stack
while (ptr || top>0) {
if (ptr) {
stack[top++]=ptr;//push
ptr=ptr->left;
}
else {
ptr=stack[--top];//ptr=stack top;pop;
printf("%c ",ptr->key);//visit root
num++;
ptr=ptr->right;
}
}
return num;
}
int depth(node *root) {
int d1,d2;
if (!root) {
return 0;
}
d1=depth(root->left);
d2=depth(root->right);
return ((d1>d2)?d1:d2) + 1;
}
node *at(node *root, key_t key_argu) {
node *ptr = root;
while (ptr->key != key_argu) {
if (ptr->left != NULL && key_argu < ptr->key ) {
ptr = ptr->left;
}
else if (ptr->right != NULL && ptr->key < key_argu ) {
ptr = ptr->right;
}
else {
return NULL;
}
}
return ptr;
}
node *append(node *root, key_t key_argu) { // left is prior
node *ptr = root, *nptr;
//empty
if (root->left == NULL && root->right==NULL) {
root->key = key_argu;
return root;
}
//non-empty
//seek position
while (ptr->key != key_argu) {
if (key_argu < ptr->key ) {
if (ptr->left) {
ptr = ptr->left;
}
else {
break;
}
}
else if ( ptr->key < key_argu ) {
if (ptr->right) {
ptr = ptr->right;
}
else {
break;
}
}
}
nptr=append_node(ptr,key_argu);
return nptr;
}
int rm0(node *root,node *ptr) {//no-root and with a full sub tree
node *temp;
temp = ptr;
// replace ptr for ptr->left sub tree
if (ptr == ptr->prior->left) {
ptr->prior->left = ptr->left;
}
else if (ptr == ptr->prior->right) {
ptr->prior->right = ptr->left;
}
//seek position for ptr->right sub tree
ptr = ptr->left; //update ptr
while (ptr->right) {
ptr = ptr->right;
}
ptr->right = temp->right;
free(temp);
return 0;
}
node *rm(node *root,key_t key_argu) { //cannot
node *ptr;
ptr = at(root,key_argu);
if (ptr->left == NULL && ptr->right == NULL) {
if (ptr == ptr->prior->left) {
ptr->prior->left = NULL;
}
else {
ptr->prior->right = NULL;
}
free(ptr);
}
else if (ptr->left != NULL && ptr->right == NULL) {
if (ptr == ptr->prior->left) {
ptr->prior->left = ptr->left;
}
else {
ptr->prior->right = ptr->left;
}
free(ptr);
}
else if (ptr->left == NULL && ptr->right != NULL) {
if (ptr == ptr->prior->left) {
ptr->prior->left = ptr->right;
}
else {
ptr->prior->right = ptr->right;
}
free(ptr);
}
else if (ptr->left ==! NULL && ptr->right != NULL) {
rm0(root,ptr);
}
return root;
}
int clear(node *root) {
node *ptr=root,*temp;
if (ptr) {
temp = ptr->right;
clear(ptr->left);
free(ptr);
clear(temp);
}
}
int clear2(node *root) {
node *ptr=root,*temp;
node *stack[1024]={NULL};
int top=0;//top of stack
while (ptr || top>0) {
if (ptr) {
stack[top++]=ptr;//push
ptr=ptr->left;
}
else {
ptr=stack[--top];//ptr=stack top;pop;
temp = ptr;
ptr=ptr->right;
free(temp);
}
}
return num;
}
int main(void) {
char ch;
int n;
node *temp,*root=(node *)malloc(sizeof(node));
examp_tree(root);
//display
num=0;
n=traver(root);
printf("%d\n",n);
return 0;
}