【C++、数据结构】二叉排序树(二叉查找树、二叉搜索树)(图解+完整代码)

目录

[⚽1.什么是二叉排序树]

[🏐2.构建二叉排序树]

[🏀3.二叉排序树的查找操作]

[🥎4.二叉排序树的删除]

[🎱5.完整代码]

⚽1.什么是二叉排序树

二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:

  • 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
  • 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
  • 它的左右子树也分别为二叉搜索树
    在这里插入图片描述

🏐2.构建二叉排序树

  1. 二叉搜索树的模拟实现

2.1 结点的声明

//描述二分查找树的一个结点
template<class K>
struct BSTNode
{
   
	K _key;						//数据域
	struct BSTNode* _left;		//指向左子树指针
	struct BSTNode* _right;		//指向右子树指针
	BSTNode(K key)
		:_key(key)
		,_left(nullptr)
		,_right(nullptr)
	{
   }
};

2.2 基本的几个成员函数

template<class K>

class BSTree
{
                  
	typedef BSTreeNode<K> Node;
private:
	//没有参数是不能递归的
	void DestroyTree(Node* root)
	{
   
		if (root == nullptr)
			return;
		DestroyTree(root->_left);
		DestroyTree(root->_right);
		delete root;
	}

	Node* CopyTree(Node* root)
	{
   
		if (root == nullptr)
			return nullptr;

		Node* copyNode = new Node(root->_key);
		copyNode->_left = CopyTree(root->_left);
		copyNode->_right = CopyTree(root->_right);
		
		return copyNode;
	}
public:
	//强制编译器自己生成构造函数 -- C++11
	BSTree() = default;

	/*BSTree()
		:_root(nullptr)
	{}*/

	//前序遍历递归拷贝
	BSTree(const BSTree<K>& t)
	{
   
		_root = CopyTree(t._root);
	}

	//t1 = t2; -- 任何赋值重载都可以用现代写法
	BSTree<K>& operator=(BSTree<K> t)
	{
   swap(_root, t._root);
		return *this;
	}

	~BSTree()
	{
   
		DestroyTree(_root);
		_root = nullptr;
	}

构造函数

  • 这里我们可以采用传统的方法
  • 直接初始化成员变量
  • 也可以用C++11的语法default
  • 强制编译器自己生成构造函数

拷贝构造

  • 这里我们用了递归的方式进行拷贝
  • 采用根 - 左 - 右 的前序遍历的递归方式对整个二叉树拷贝
  • 最后将跟结点返回

析构函数

  • 析构函数我们这里也是采用递归的方式进行一个一个结点析构
  • 同样的我们再嵌套一个子函数
  • 也是采用类似前序遍历的方法将整个二叉树释放掉

采用递归方式的缺点就是如果数的结点个数足够多的时候,就会有爆栈的风险!!

2.3 插入操作

假设我们有以下数据,我们按从左到右的顺序来构建二叉排序树:

  1. 首先,将8作为根节点
  2. 插入3,由于3小于8,作为8的左子树
  3. 插入10,由于10大于8,作为8的右子树
  4. 插入1,由于1小于8,进入左子树3,1又小于3,则1为3的左子树
  5. 插入6,由于6小于8,进入左子树3,6又大于3,则6为3的右子树
  6. 插入14,由于14大于8,进入右子树10,14又大于10,则14为10的右子树
  7. 插入4,由于4小于8,进入左子树3,4又大于3,进入右子树6,4还小于6,则4为6的左子树
  8. 插入7,由于7小于8,进入左子树3,7又大于3,进入右子树6,7还大于于6,则7为6的右子树
  9. 插入13,由于13大于8,进入右子树10,又13大于10,进入右子树14,13小于14,则13为14的左子树

经过以上的逻辑,这棵二叉排序树构建完成。

我们可以看出:

  • 只要左子树为空,就把小于父节点的数插入作为左子树
  • 只要右子树为空,就把大于父节点的数插入作为右子树
  • 如果不为空,就一直往下去搜索,直到找到合适的插入位置

没错,这棵二叉树中序遍历结果为:

  • 二叉树中序遍历结果为升序,左节点<根节点<右节点

插入思路:

  • 从根结点开始遍历。(不能相等哦,直接结束就好
  • key<遍历结点的值,则遍历其左子树;key>遍历结点的值,则遍历其右子树
  • 直到遍历到某个叶子结点
  • 插入:比叶子节点小,插入左子树,反之,右子树

根据以上思路,我们其实就可以写出代码了,构建的过程其实就是插入的过程:

//插入函数
bool Insert(const K key)
{
   
	Node* newnode = new Node(key);
	//空树时
	if (_root == NULL)
	{
   
		_root = newnode;
		return true;
	}
	Node* cur = _root;			//用来遍历
	Node* parent = nullptr;		//记录上一个节点
	while (cur)
	{
   
		parent = cur;
		//key< 结点值,遍历其左子树
		if (key < cur->_key)
		{
   
			cur = cur->_left;
		}
		//key> 结点值,遍历其右子树
		else if (key > cur->_key)
		{
   
			cur = cur->_right;
		}
		//不能插入相同的
		else
		{
   
			return false;
		}
	}
	if (key < parent->_key)
	{
   
		parent->_left = newnode;
	}
	if (key > parent->_key)
	{
   
		parent->_right = newnode;
	}
	return true;
}

🏀3.二叉排序树的查找操作

它既然也叫二叉查找树,那想必会非常方便我们查找吧!它的操作并不是把中序遍历的结果存入数组,然后在有序数组里查找,而是直接在树上查找。

  1. 首先,访问根节点8
  2. 根据性质,7<8访问8的左子树
  3. 访问到了3,7>3,访问3的右子树
  4. 访问到了6,继续访问6的右子树
  5. 访问到了7,刚好找到啦!

显然,它的效率会比在无序数组中挨着查找快多了吧!我们直接上代码。

	//查找
	bool Find(const K& key)
	{
   
		Node* cur = _root;
		while</
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

代码探秘者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值