算法导论例程——二叉搜索树

本文介绍了二叉搜索树的概念及其性质,包括每个节点的左子节点小于节点本身,右子节点大于节点本身。重点讲述了如何在二叉搜索树中进行节点的插入和删除操作,插入操作通过遍历到叶节点找到合适位置,删除操作则分为无子节点、只有一个子节点和有两个子节点三种情况处理。
摘要由CSDN通过智能技术生成

二叉树的定义是递归的:每一个结点都至多含有左右两个子结点。二叉树是无向无环图,度不大于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;
}


接下来是比较麻烦一些的删除操作,由于删除后需要移动其他节点来维护二叉树的性质,所以我们先封装这样一个trans_tree函数来辅助完成移动节点的操作。

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;
	}
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值