一、二叉排序树的定义
二叉排序树(Binary Search Tree)是一种特殊的二叉树,它的左子树中的所有节点都比根节点小,右子树中的所有节点都比根节点大。 总之,对于树中的每个节点,其左子树中的所有节点的值都小于它,其右子树中的所有节点的值都大于它。
typedef struct Node
{
int data;
struct Node *right,*left;
}*BiTree;
二、二叉排序树的构造
1. 递归插入节点
二叉排序树可以通过递归插入节点来构造,在插入节点的时候需要比较节点的值与当前节点的值的大小关系,以此来确定节点的插入位置。
void insertBST(BiTree *root,int data)
{
if(!(*root))
{
*root=(Node*)malloc(sizeof(Node));
(*root)->data=data;
(*root)->left=(*root)->right=NULL;
return;
}
if(data==(*root)->data)
{
return;
}
else if(data<(*root)->data)
{
insertBST(&(*root)->left,data);
}
else if(data>(*root)->data)
{
insertBST(&(*root)->right,data);
}
}
当插入一个节点时,如果二叉排序树为空,则将该节点作为根节点插入; 当插入的节点的值等于二叉排序树的根节点的值时,不进行任何操作; 当插入的节点的值小于二叉排序树的根节点的值时,递归地插入在左子树中; 当插入的节点的值大于二叉排序树的根节点的值时,递归地插入在右子树中。
2. 非递归插入节点
除了递归插入节点,我们还可以通过非递归方式插入节点来构造二叉排序树,方法是利用while循环周期性地遍历二叉排序树的节点,直到找到插入位置。
void insertBST(BiTree *root,int data)
{
BiTree p=*root,q;
if(!p)
{
*root=(Node*)malloc(sizeof(Node));
(*root)->data=data;
(*root)->left=(*root)->right=NULL;
return;
}
while(p)
{
if(data==p->data)
{
return;
}
q=p;
if(data < p->data)
{
p=p->left;
}
else
{
p=p->right;
}
}
p=(BiTree)malloc(sizeof(Node));
p->data=data;
p->left=p->right=NULL;
if(data < q->data)
{
q->left=p;
}
else
{
q->right=p;
}
}
先定义一个p指针指向根节点,如果p为空,则将新节点作为根节点插入;若p不为空,则进行while循环。对于每个节点p,我们比较插入的值与p的值大小,以此来确定插入位置。如果查找到相同节点的值,则不进行插入;如果移向了空节点,则插入新节点作为其子节点。
三、二叉排序树的遍历
遍历二叉排序树可以得到一个有序的数列,可以按照从小到大或从大到小的顺序输出节点值。
1. 中序遍历
中序遍历是一种常用的遍历方式,中序遍历二叉排序树可以得到有序的节点序列。
void inOrder(BiTree root)
{
if(root!=NULL)
{
inOrder(root->left);
printf("%d ",root->data);
inOrder(root->right);
}
}
从根节点开始,先遍历左子树,然后输出根节点,最后遍历右子树。
2. 前序遍历
前序遍历从根节点开始遍历,先输出根节点,然后访问左子树,最后遍历右子树。
void preOrder(BiTree root)
{
if(root!=NULL)
{
printf("%d ",root->data);
preOrder(root->left);
preOrder(root->right);
}
}
3. 后序遍历
后序遍历从左叶子结点开始后序遍历,然后是右子树,最后是根节点。
void postOrder(BiTree root)
{
if(root!=NULL)
{
postOrder(root->left);
postOrder(root->right);
printf("%d ",root->data);
}
}
四、二叉排序树的删除
二叉排序树的删除分为三种情况:
- 被删除节点无子节点,直接删除即可;
- 被删除节点仅有一个子节点,将其子节点作为其父节点的子节点即可;
- 被删除节点有两个子节点,需要寻找其前驱或后继来替代被删除节点,并删除前驱或后继节点。
1. 寻找前驱节点
寻找前驱节点是指在二叉排序树中,寻找比被删除节点的值小的最大节点。要做到这一点,我们需要在被删除节点的左子树中,找到值最大的节点,即左子树的最右下角节点。
BiTree findMax(BiTree root)
{
while(root->right!=NULL)
{
root=root->right;
}
return root;
}
2. 寻找后继节点
寻找后继节点是指在二叉排序树中,寻找比被删除节点的值大的最小节点。要做到这一点,我们需要在被删除节点的右子树中,找到值最小的节点,即右子树的最左下角节点。
BiTree findMin(BiTree root)
{
while(root->left!=NULL)
{
root=root->left;
}
return root;
}
3. 删除节点
删除节点需要对被删除节点的子树进行重连,也就是将被删除节点的父节点、子节点与被删除节点割离,然后对两部分子树进行合并。
void deleteNode(BiTree *root,int data)
{
if((*root)==NULL)
{
return;
}
if((*root)->data > data)
{
deleteNode(&((*root)->left),data);
}
else if((*root)->data < data)
{
deleteNode(&((*root)->right),data);
}
else
{
if((*root)->left==NULL && (*root)->right==NULL)
{
(*root)=NULL;
}
else if((*root)->left==NULL)
{
(*root)=(*root)->right;
}
else if((*root)->right==NULL)
{
(*root)=(*root)->left;
}
else
{
BiTree max=findMax((*root)->left);
(*root)->data=max->data;
deleteNode(&((*root)->left),max->data);
}
}
}
首先比较删除节点的值与当前节点的值的大小关系,递归查找要删除的节点,直到找到该节点;对于待删除节点是否有子节点,分别进行不同的操作。如果没有子节点,则直接删除该节点;如果只有一个子节点,则将该子节点作为当前节点的子节点;如果有两个子节点,则找到该节点的前驱或后继节点来替换此节点,并删除前驱或后继节点。