二叉搜索树、二叉查找树、二叉排序树的插入,查找,删除,遍历

一、二叉搜索树的概念

1、定义

二叉搜索树,二叉查找树,(又称为二叉排序树),它满足如下四点性质:

1)空树是二叉搜索树;

2)若它的左子树不为空,则左子树上所有结点的值均小于它根结点的值

3)若它的右子树不为空,则右子树上所有结点的值均大于它根结点的值;

4)它的左右子树均为二叉搜索

 2、用途

从二叉搜索树的定义可知,它的前提是二叉树,并且采用了递归的方式进行定义,它的结点间满足一个偏序关系,左子树根结点的一定比父结点小,右子树根结点的值定比父结点大。

正如它的名字所说,构造这样一棵树的目的是为了提高搜索的速度,如果对二叉搜索树进行中序遍历,我们可以发现,得到的序列是一个递增序列。 

二、代码实现 

1、创建树节点

template<typename T>
struct TreeNode {//树节点
	T val;
	TreeNode* left;
	TreeNode* right;
	TreeNode() :val(0), left(NULL), right(NULL) {}
	TreeNode(T x) :val(x), left(NULL), right(NULL) {}
};

创建一个树节点,包括数据域,左子树节点,右子树节点和树结点的构造函数。

2、创建二叉搜索树

template<typename T>
class BinaryTreeSearch {//二叉搜索树
private:
	TreeNode<T>* root;
	TreeNode<T>* insertNode(TreeNode<T>* node, T value);//插入
	TreeNode<T>* removeNode(TreeNode<T>* node, T value);//删除
	bool searchNode(TreeNode<T>* node, T value);//搜索
	void inOrder(TreeNode<T>* node);//中序遍历
};

 二叉搜索树中包括根节点和一些对这棵树进行操作的函数,插入节点,删除节点,搜索,以及中序遍历。

下面是提供给外部的一些接口:

public:
	BinaryTreeSearch() :root(NULL){}
	~BinaryTreeSearch();

	void insert(T value) {
		root = insertNode(root, value);
	}

	void remove(T value) {
		root = removeNode(root, value);
	}

	bool search(T value) {
		return searchNode(root, value);
	}

	void inOrderTraversal() {
		inOrder(root);
		cout << endl;
	}

 3.insertNode插入节点

template<typename T>//插入
TreeNode<T>* BinaryTreeSearch<T>::insertNode(TreeNode<T>* node, T value) {
	if (node == NULL) return new TreeNode<T>(value);
	if (value < node->val)
	{
		node->left = insertNode(node->left, value);
	}
	else if (value > node->val)
	{
		node->right = insertNode(node->right, value);
	}
	return node;
}

因为这是一个二叉搜索树,所以会在插入时就进行排序,如果插入值大于当前节点的值,就放在右子树,如果小于,就放在左子树。通过递归,当递归到当前树节点为空时,就返回一个有参构造后的树节点,链接在父节点的对应位置。最后,当递归结束后,返回根节点给root。

4.searchNode搜索

对于一颗二叉搜索树,搜索的功能肯定是必不可少的

template<typename T>//搜索
bool BinaryTreeSearch<T>::searchNode(TreeNode<T>* node, T value) {
	if (node == NULL) return false;
	if (value < node->val) {
		return searchNode(node->left, value);
	}
	else if (value > node->val) {
		return searchNode(node->right, value);
	}
	return true;
}

同样,当查找值大于当前节点的值时,走右子树,小于则走左子树,通过不断地递归,当查找值既不大于节点值也不小于时,就说明在当前二叉树中找到了该值,返回true;如果递归到当前节点等于NULL时,就说明递归到了叶子节点,就说明没有找到该值,返回false。

5.inOrder中序遍历

template<typename T>//中序遍历
void BinaryTreeSearch<T>::inOrder(TreeNode<T>* node) {
	if (node) {
		inOrder(node->left);
		cout << node->val << " ";
		inOrder(node->right);
	}
}

 这个没什么好说的,看代码叭。

6.removeNode删除!!!

这一段代码就很值得讨论了。首先,删除值为 val 的结点的过程,从根结点出发,总共七种情况依次判断:

第1步、如果当前结点为空,则直接返回空树,

第2步、如果要删除的值小于当前结点的值,则递归调用左子树进行结点的删除

第3步、如果要删除的值大于当前结点的值,则递归调用右子树进行结点的删除。

第4步、如果当前结点是一个叶子结点,即它没有左子树和右子树,那么删除该结点,并且返
回空树。

第5步、如果当前结点只有右子树而没有左子树,那么删除当前结点并将右子树返回,

第6步、如果当前结点只有左子树而没有右子树,那么删除当前结点并将左子树返回,

第7步、如果当前结点既有左子树又有右子树,那么找到当前结点右子树中的最小值结点(即最左边的结点),将当前结点的值替换为这个最小值结点的值,然后递归地删除右子树中该最小值结点。 

对于删除节点这一过程来说,用递归去找到需要删除的节点 即:

	if (node == NULL)return NULL;

	if (value < node->val) {
		node->left = removeNode(node->left, value);
	}
	else if (value > node->val) {
		node->right = removeNode(node->right, value);
	}
    else{
    .......//找到需要删除的节点了
            }

通过不断递归,找到需要删除的节点,如果该节点为空,则直接返回NULL;

找到需要删除的节点后 ,如果该节点为叶子节点,则直接删除节点,然后返回NULL;

if (node->left == NULL && node->right == NULL) {//删除叶子节点
			delete node;
			return NULL;
		}

如果该节点有左子树或者右子树,则先保存其左or右子树,然后删除节点,并返回其左or右子树:

else if (node->left == NULL) {//左子树为空,右子树不为空
			TreeNode<T>* rightChild = node->right;
			delete node;
			return rightChild;
		}
		else if (node->right == NULL) {//右子树为空,左子树不为空
			TreeNode<T>* leftChild = node->left;
			delete node;
			return leftChild;
		}

如果左右子树均不为空,则 在其右子树中找到最小值后,把值赋给需要删除的节点,并把最小值的节点删除。

解释:因为二叉搜索树的右子树中任意值肯定大于左子树中任意值,要保证二叉搜索树这一特征,且通过中序遍历后 是一个升序排列,所以要找一个大于左子树但是小于右子树的值来作为根节点,那么,右子树中的最小值就再合适不过了。

else {//左右子树均不为空
			TreeNode<T>* replacement = node->right;
			while (replacement->left) {
				replacement = replacement->left;
			}
			node->val = replacement->val;
			node->right = removeNode(node->right, replacement->val);
		}

其中,replacement为右子树中的最小值的节点,将replacement的值赋给需要删除的节点,就能保证二叉搜索树的特征,然后,通过removeNode删除replacement 。

下面用一个GIF来演示这一过程:

下面是删除节点的完整代码:

template<typename T>//删除
TreeNode<T>* BinaryTreeSearch<T>::removeNode(TreeNode<T>* node, T value) {
	if (node == NULL)return NULL;

	if (value < node->val) {
		node->left = removeNode(node->left, value);
	}
	else if (value > node->val) {
		node->right = removeNode(node->right, value);
	}
	else {
		if (node->left == NULL && node->right == NULL) {//删除叶子节点
			delete node;
			return NULL;
		}
		else if (node->left == NULL) {//左子树为空,右子树不为空
			TreeNode<T>* rightChild = node->right;
			delete node;
			return rightChild;
		}
		else if (node->right == NULL) {//右子树为空,左子树不为空
			TreeNode<T>* leftChild = node->left;
			delete node;
			return leftChild;
		}
		else {//左右子树均不为空
			TreeNode<T>* replacement = node->right;
			while (replacement->left) {
				replacement = replacement->left;
			}
			node->val = replacement->val;
			node->right = removeNode(node->right, replacement->val);
		}
	}
	return node;
}

三、代码测试

 和set一样,对于相同的值,是无法插入的。通过中序遍历,可以发现,原来无序的插入,最后变成了有序的。

删除节点:

查找:

  • 13
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值