概述:
二叉搜索树中的关键词总是以满足二叉搜索树性质的方式来存储:
设x是二叉搜索树中的一个结点。如果y是x左子树中的一个结点,那么y.key<=x.key。如果y是x右子树中的一个结点,那么y.key>=x.key。
Notes:不同于一般二叉树,二叉搜索树的顺序与结点内容即key值有关,而一般二叉树对相应结点的key值没有要求,故若要对一般二叉树进行遍历,则要排序,因此才有常见的先序,中序,后序三种遍历方式
这样一棵树可以使用一个链表数据结构来表示。
对二叉搜索树的基本操作有:
查找(基于关键词、最小值、最大值、后继、前驱)、插入(考虑空树情况)、删除
(时间复杂度均为O(h), h为树的高度)
代码实现
#include<stdio.h>
#include<stdlib.h>
typedef int ElemType;
typedef struct BiTree{//也即三叉链表 二叉链表少了双亲域
ElemType key;//结点值
struct BiTree *left;//左孩子
struct BiTree *right;//右孩子
struct BiTree *p;//双亲
}BiTNode,*BiTree;
/*基于关键词的查找*/
//递归表示
BiTree Search_BST(BiTree T, ElemType k)
{
if (T != NULL&&T->key != k)
{
if (k < T->key)
return Search_BST(T->left,k);
else
return Search_BST(T->right,k);
}
return T;
}
//迭代表示
BiTree Interative_Search_BST(BiTree T, ElemType k)
{
while (T != NULL&&T->key != k)
{
if (k < T->key)
T = T->left;
else
T = T->right;
}
return T;
}
/*最大关键字元素和最小关键字元素的查找*/
BiTree Tree_Minimum(BiTree T)
{
while (T->left != NULL)//找到最左下的元素
T = T->left;
return T;
}
BiTree Tree_Maximum(BiTree T)
{
while (T->right != NULL)//找到最右下的元素
T = T->right;
return T;
}
/*对某结点的后继和前驱的查找*/
BiTree Tree_Successor(BiTree T)//查找后继
{
if (T->right != NULL)//T有右孩子,返回右子树的最小值
return Tree_Minimum(T->right);
BiTree y;
y = T->p;//y为T的双亲
while (y != NULL&&T == y->right)//直到找到一个结点是这个结点的双亲的左孩子 返回这个双亲
{
T = y;
y = y->p;
}
return y;
}
BiTree Tree_Predecessor(BiTree T)//查找前驱
{
if (T->left != NULL)
return Tree_Maximum(T->left);
BiTree y;
y = T->p;
while (y != NULL&&T == y->left)
{
T = y;
y = y->p;
}
return y;
}
/*二叉搜索树的插入*/
//note:要对搜索树结构进行修改
void InsertBST(BiTree &T, ElemType e)//传入二叉树的根结点地址 note:《算法导论》传的是T.root 说明定义了另一个结构体存树的相关信息 如根结点地址 结点数等
{
BiTree z, x, y=NULL;
//对新增结点进行初始化,现在要修改的是它的双亲和它双亲的left或right
z = (BiTree)malloc(sizeof(BiTNode));
if (z != NULL)//空间申请成功
{
z->key = e;
z->left = z->right=NULL;
}
x = T;
while(x != NULL)
{
y = x;
if (z->key < x->key)//z应该插入到左半部分
x = x->left;
else
x = x->right;
}//循环跳出后 x=NULL, x的位置即z要插入的位置 y的位置为z的双亲的位置
//下面处理z和y的关系
z->p = y;
if (y = NULL)//说明是空树
T = z;//新插入的z即为根结点
else if (z->key < y->key)//z为y左孩子
y->left = z;
else
y->right = z;
}
/*这个函数用一个以v为根的子树来替换一个以u为根的子树,二叉树的删除操作要用到*/
void Transplant(BiTree &T, BiTree u, BiTree v)
{
if (u->p == NULL)//u没有双亲,说明u为根结点
T = v;
else if (u->p->left == u)//若u是其双亲的左子树
u->p->left = v;
else
u->p->right = v;
if (v != NULL)//要考虑v是否为空
v->p = u->p;
}
/*对二叉树的删除操作*/
void DeleteBST(BiTree &T, BiTree z)//以T为根结点的二叉搜索树中删除结点z
{
if (z->left == NULL)//第一种情况:要删除的结点没有左孩子 包含了z只有右孩子和右孩子为NULL即z没有孩子的情况 ,z右孩子代替z
Transplant(T, z, z->right);
else if (z->right == NULL)//第二种情况:z有且仅有非NULL左孩子(因为左孩子为NULL则说明没有孩子,这种情况上面已经包含了)z左孩子代替z
Transplant(T, z, z->left);
else
{
BiTree y = Tree_Minimum(z->right);
if (y != z->right)//若z的右孩子不是z的后继 说明以右孩子为根的子树最小的那个结点才是z的后继 这个时候这个结点一定没有左孩子!!!
{
Transplant(T, y, y->right);//用y自己的右孩子来替代y
// y->p = z->p;//???这一步不用吗???不用 因为后面还要将y Transplant到z 故只用处理好p的子树部分即可
y->right = z->right;//并置y为z的右孩子的双亲
y->right->p = y;
}//处理完以后继结点y为根的子树后(包含本来y就是z的右孩子的情况)
Transplant(T, z, y);
y->left = z->left;
y->left->p = y;
}
}
notes:
1.查询二叉搜索树,对于大多数计算机,用迭代方式比用递归效率要高得多。
2.《算法导论》中函数传入的是T.root 应该有个类似下面形式的结构体定义,T是Tree类型的变量,我的函数中传入的T默认是根结点地址。
typedef struct{
BiTree root;//根结点地址
int nodenum;//结点数量
//其他信息,如树的类型(二叉搜索树,红黑树,AVL树,B-/B+树)blablabla
}Tree;