【数据结构】二叉搜索树

一. 二叉搜索树的特点

  1. 若左子树不为空,左子树的值小于等于根节点
  2. 若右子树不为空,右子树的值大于等于根节点
  3. 他的左右子树也都是二叉搜索树
  4. 二叉搜索树可以支持插入相同的值,也可以支持插入不同的值,map/set/multimap/multiset系列容器底层就是二叉搜索树,multi系列支持插入相同的值(本章节我实现的二叉树代码不支持插入相同值)

二. 二叉树的性能

  1. 最优:如果是个完全二叉树,高度是 logN
  2. 最差:单支树,高度是 N

注意 这里和二分查找对比一下,二分查找也可以实现 O(logN) 的查找,但是二分查找有两个比较大的缺陷

  • 需要有序,并且存储在可以下表访问的结构中
  • 插入删除需要挪动数据,效率低
  • 在这里插入图片描述

三. 二叉树的实现

(1) 二叉树的实现的基本构成

由链表实现,节点内保存左右结点的地址

template <class K>
struct BSTnode
{
	K _key;
	BSTnode<K>* _left;
	BSTnode<K>* _right;
	BSTNode(const K& key)
		:_key(key)
		,_left(nullptr)
		,_right(nullptr)
	{}
};
template<class K>
struct BSTtree
{
public:
	typedef BSTnode<K> Node;
private:
	Node* _root = nullptr;
};

(2) 二叉树的插入

  1. 树为空,直接新增节点,节点地址复制给root指针
  2. 树不空,插⼊值比当前结点⼤往右走,插⼊值比当前结点小往左走,找到空位置,插入新节点
    在这里插入图片描述
bool Insert(const K& key)
{
	if (_root == nullptr)
	{
		_root = new Node(key);
		return true;
	}
	Node* parent = nullptr;
	Node* cur = _root;
	while (cur)
	{
		if (key > cur->_key)
		{
			parent = cur;
			cur = cur->_right;
		}
		else if (key < cur->_key)
		{
			parent = cur;
			cur = cur->_left;
		}
		else   //节点存在就不再插入
			return false;
	}
cur = new Node(key); 
//判断在父节点的右还是左
if (parent->_key < key)
{
	parent->_right = cur;
}
else 
{
	parent->_left = cur;
}
return true;
}

(3) 二叉树的查找

  1. 从根节点查找,比根的值大就往右边走,比根的值小就往左边走
  2. 最多查找高度次,如果找到空还没有,就是树里面没有这个值
    这里跟插入逻辑一样
bool Find(const K& key)
{
	Node* cur = _root;
	while (cur)
	{
		if (cur->_key < key)
		{
			cur = cur->_right;
		}
		else if (cur->_key > key)
		{
			cur = cur->_left;
		}
		else
		{
			return true;
		}
	}
	return false;
}

(4) 二叉树的删除(最重要,重点看)

  1. 首先要查找要删除的值存不存在,如果不存在就返回false。
  2. 如果存在,删除节点不能破坏二叉树特点,就分为两种情况
    • 要删除的节点N左右子树都为空或有一方为空,把N的非空节点(或空节点)给N的父节点,
    • 要删除的N节点左右都不为空,选N节点右子树的最小值(最左节点)或者左子树的最大节点(最右节点),和N节点交换值,然后删除被交换的节点,(被交换的节点一定符合第一种情况可以直接删)
      在这里插入图片描述
bool Erase(const K& key)
{

	Node* parent = nullptr;
	Node* cur = _root;
	while (cur)
	{
		if (key > cur->_key)
		{
			parent = cur;
			cur = cur->_right;
		}
		else if (key < cur->_key)
		{
			parent = cur;
			cur = cur->_left;
		}
		else
		{
			if (cur->_left == nullptr)
			{
				//这里要考虑删除节点为根节点的情况
				if (parent == nullptr)
				{
					_root = cur->_right;
				}
				else
				{
					if (parent->_left == cur)
						parent->_left = cur->_right;
					else
						parent->_right = cur->_right;
				}
				delete cur;
				return true;
			}
			//这里是cur右节点为空的情况,就不实现了,逻辑和左节点为空的一样
			 else if (cur->_right == nullptr)
				;
			//左右节点都不为空
			 else
			{ 
				//寻找右树最小节点(最左节点)
				Node* rightMinP = cur;
				Node* rightMin = cur->_right; 
				while (rightMin->_left) 
				{
					rightMinP = rightMin; 
					rightMin = rightMin->_left;
				}
				//把右数的最小值赋给要删除的节点,然后删除右数的最小节点
				cur->_key = rightMin->_key;
				if (rightMinP->_left == rightMin)
					rightMinP->_left = rightMin->_right; 
				else
					rightMinP->_right = rightMin->_right; 
				delete rightMin;
				return true;
			}
		}
	}
	return false;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值