二叉搜索树:查找,插入,删除操作

目录

什么是二叉搜索树

二叉搜索树的查询

关键字查找

查找最小值

查找最大值

查找前驱

查找后继

插入

删除


 

 

什么是二叉搜索树

894dcfcc4fe14a0c94932f04f4a37b12.png

二叉搜索树是由二叉树组织的,如下:和普通二叉树相比多了parent结点,这样可以匹配搜索二叉树性质方便实现相应的功能;二叉搜索树有那些性质呢:

对任何结点x,其左子树中的关键字最大不超过x.key,其右子树中的关键字最小不低于x.key.

class Node {
public:
	int key;
	node*left;
	node*right;
	node*parent;
};

我们可以使用中序遍历方法遍历这颗树,这样结果就是有序的

上图所示搜索二叉树中序遍历结果为:1,2,3,5,6,7,13

void InOrderTreeWalk(Node* root) {
	if (root == nullptr)return;
	InOrderTreeWalk(root->left);
	cout << root->key << " ";
	InOrderTreeWalk(root->right);
}

二叉搜索树的查询

二叉搜索树的查询有多种,如查找关键字,最小值,最大值,前驱(中序遍历中该结点前一个结点),后继(中序遍历中该结点的下一结点);

关键字查找

根据二叉搜索树的性质,我们很容易想到,结点左边数小于它,右边反之,所以找到就返回,小于去左树,大于去右树

递归代码:

Node *TreeSearch(Node*root, int k) {
	if (root == nullptr || root->key == k) {
		return root;
	}
	if (k < root->key) {
		return TreeSearch(root->left, k);
	}
	return TreeSearch(root->right, k);
}

非递归代码:

Node *IterativeTreeSearch(Node*root, int k) {
	Node*x = root;
	while(x != nullptr && x->key != k) {
		if (x->key < k) {
			x = x->left;
		}
		else {
			x = x->right;
		}
	}
	return x;
}

查找最小值

因为结点左边值小于它,右边大,所以只需返回最左结点,我们假设根节点不为空

Node *TreeMinimum(Node*root) {
	Node*x = root;
	while (x->left != nullptr) {
		x = x->left;
	}
	return x;
}

查找最大值

最大值一定是最右结点

Node *TreeMaximum(Node*root) {
	Node*x = root;
	while (x->right != nullptr) {
		x = x->right;
	}
	return x;
}

查找前驱

d6f4ae1cdd9b4d3ab79421e7ab4c0d8b.png

 如果一颗二叉搜索树中所有关键字都不相同,那么结点x的前驱是小于x.key的最大关键字结点,二叉搜索树允许我们不通过比较就可以确定结点前驱,如果前驱存在,下面过程会返回前驱,否则返回NULL

Node*PreSuccessor(Node*root) {
	if (root->left != nullptr) {
		return TreeMaximum(root->left);
	}
	Node*x = root, *y = root->parent;
	while (y != nullptr&&x == y->left) {
		x = y;
		y = y->parent;
	}
	return y;
}

当x有左树时,前驱为左树最大结点,直接调用maximum即可,如上图结点13

当x无左树并且有前驱y时,y就是x最底层祖先,y的右孩子也是它的祖先,只需从x沿树而上直到遇到它是双亲的右孩子,如上图结点9

查找后继

后继与前驱查找方式一样,就不具体介绍了

当x有右树时,后继为右树最小结点

当x无右树并且有后继y时,y就是x最底层祖先

Node*Successor(Node*root) {
	if (root->right != nullptr) {
		return TreeMinimum(root->right);
	}
	Node*x = root, *y = root->parent;
	while (y != nullptr&&x == y->right) {
		x = y;
		y = y->parent;
	}
	return y;
}

插入

插入比较简单,搜索到该插入的位置,判断是左树还是右树

void TreeInsert(Node*root, Node*v) {
	Node*y = nullptr, *x = root;
	while (x != nullptr) {
		y = x;
		if (v->key < x->key) {
			x = x->left;
		}
		else {
			x = x->right;
		}
	}
	v->parent = y;
	if (y == nullptr) {
		root = v;
	}
	else if (v->key < y->key) {
		y->left = v;
	}
	else {
		y->right = v;
	}
}

删除

删除操作比较复杂,我们将分为以下三种情况:

 如果z没有孩子结点,只删除z,修改父节点

 如果z只有一个孩子,就用该孩子替换z位置,修改父节点

 如果z有两个孩子,就找z的前驱或后继y,(下面我找的后继),让y替换z,这里需要判断y是否是z的右孩子

d6f4ae1cdd9b4d3ab79421e7ab4c0d8b.png

但在处理时有四种情况:

  如果z没有左孩子,就用右孩子替换,包括右孩子为空;如结点9

  如果z有做没右,就用左孩子替换;如13

  如果z有两个孩子,查找后继y(后继一定在右树且没有左孩子):

    1)如果y是右孩子,用y替换z,

    2)y不是右孩子,就先用y右孩子替换y,再用y替换z;如7

下面代码定义了一个移动子树过程

void TransPlant(Node*root, Node*z, Node*y) {
	if (z->parent == nullptr) {
		root = y;
	}
	else if (z == z->parent->left) {
		z->parent->left = y;
	}
	else {
		z->parent->right = y;
	}
	if (y != nullptr) {
		y->parent = z->parent;
	}
}

void TreeDelete(Node*root, Node*z) {
	if (z->left == nullptr) {
		TransPlant(root, z, z->right);
	}
	else if (z->right == nullptr) {
		TransPlant(root, z, z->left);
	}
	else {
		Node*y = TreeMinimum(z->right);
		if (y->parent != z) {
			TransPlant(root, y, y->right);
			y->right = z->right;
			y->right->parent = y;
		}
		TransPlant(root, z, y);
		y->left = z->left;
		y->left->parent = y;
	}
}

二叉搜索树就写到这里,欢迎大家提问讨论

 

 

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值