二叉树的定义是递归的:每一个结点都至多含有左右两个子结点。二叉树是无向无环图,度不大于2。
二叉搜索树是二叉树的一种,它具有一下的性质:
1)任一结点的左子结点都不大于该结点;
2)任一结点的左子结点都不小于该结点;
二叉搜索树根据它的性质可以设计出一个很简单的递归算法来遍历它的每一个结点。
首先二叉树的声明如下:
typedef struct binary_tree
{
int num;
struct binary_tree *p; //parent node
struct binary_tree *left; //left child node
struct binary_tree *right; //right child node
}bin;
void inorder_tree_walk(bin *x)
{
if (x != NULL)
{
inorder_tree_walk(x->left);
printf(x->num);
inorder_tree_walk(x->right);
}
}
对567行代码,如果改成657则为先序遍历(preorder tree walk),576则为后序遍历(postorder tree walk)
接下来看如何在二叉搜索树中查询某一结点。对于给定的关键字key,若给定节点存在则输出该结点的指针,若不存在则输出NIL。
void tree_search(bin *root, int key)
{
if (root == NULL || root->num == key)
return root;
if (key < root->num)
tree_search(root->left, key);
else
tree_search(root->right, key);
}
注意到这是一个尾递归,所以我们可以把它改写成循环的形式
while (root != NULL && root->num != key)
{
if (key < root->num)
root = root->left;
else
root = root->right;
}
return root;
bin* successor(bin *key)
{
if (key->right != NULL)
{
key = key->right;
while (key->left != NULL)
key = key->left;
return key;
}
bin *y;
y = key->p;
while (y != NULL && key == y->right)
{
key = y;
y = y->p;
}
return y;
}
对二叉搜索树有两个关键的操作——插入和删除,因为在完成这些操作的时候我们还要维护二叉搜索树本身的性质。
首先看较为简单的插入操作,我们对insert函数的输入是要插入的结点的指针以及整个搜索树的根指针,我们不能改变已有的父子关系,所以插入只能遍历到叶节点来找到合适的位置。
void insert_tree(bin* root, bin* key)
{
bin* temp1;
bin* temp2;
temp1 = root;
while (temp1 != NULL)
{
temp2 = temp1;
if (key->num < temp1->num)
temp1 = temp1->left;
else
temp1 = temp1->right;
}
key->p = temp2;
if (temp2 == NULL)
root = key;
else if (key->num < temp2->num)
temp2->left = key;
else
temp2->right = key;
}
void trans_tree(bin *root, bin *target, bin *key)
{
if (target->p == NULL)
root = key;
else if (target == target->p->left)
target->p->left = key;
else
target->p->right = key;
if (key != NULL)
key->p = target->p;
}
它讨论了三种情况,被移动节点是根节点,是某一结点的左子节点或右子节点的情况,在最后要注意树的指向是双向的,我们不止要完成向下指的过程,还要指回去。
有了这个函数,我们再来看delete函数。他分了四种情况。
1)被删除节点只有右子节点:将右子节点移动过来;
2)被删除节点只有左子节点:将左子节点移动过来;
3)被删除节点左右子节点都有:找到他的后继:
a)后继是右子树的最左节点:用该节点的右子节点代替该结点(此时该结点没有左子节点),将该节点替换被删除节点并连接原来的左右子树。
b)后继是右子节点(右子树没有左子树):直接用该结点替换被删除节点。
void tree_delete(bin *root, bin* key)
{
if (key->left == NULL)
trans_tree(root, key, key->right);
else if (key->right == NULL)
trans_tree(root, key, key->left)
else
{
bin* temp = key->right;
while (temp->left != NULL)
temp = temp->left;
if (temp->p != key)
{
trans_tree(root, temp, temp->right);
temp->right = key->right;
temp->right->p = temp;
}
trans_tree(root, key, temp);
temp->left = key->left;
temp->left - p = temp;
}
}