二叉搜索数使用,底层原理及代码实现

1:二叉搜索树的定义

二叉搜索树的底层是一个二叉链表

二叉搜索树又称二叉排序树,它或者是一棵空树 ,或者是具有以下性质的二叉树 :
  1. 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
  2. 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
  3. 它的左右子树也分别为二叉搜索树

举个例子,我们要把数组[50,87,64,100,24,35,1,77]插入到二叉搜索树中,对应的树应该是这个样子

2.二叉搜索树的特点

  1. 既然以搜索树为名字,显然搜索功能是很强大的(相较于数组的搜索),对于二叉树的每一个节点来说其左子树的任意值绝对小于根节点,右子树的任意值绝对大于根节点,如果我们要查找一个值,该值比根节点小的话去左子树找,比根节点大的话去右子树找,如果二叉搜索树是一颗满二叉树的话,搜索的时间复杂度将为log(N),相当于从全世界70亿人中找一个人仅用了30次操作.
  2. 因为二叉树的左子树的任意值绝对小于根节点,右子树的任意值绝对大于根节点,所以其中序遍历即为升序数组,搜索树理论上不可以同时存在多个相同的元素,因为这是没有意义的,所以严格来说,这是一个无重复元素的升序数组

3.二叉搜索树的底层原理

插入

显然原树在执行完插入操作后仍应该是二叉搜索树,为了不破坏原树的结构,对于根节点来说,如果插入值大于根节点,应该往右插入,插入值小于根节点,应该往左插入.直到最后1.找到的节点为空,代表应该向空节点处插入2.存在节点值 == 插入值,这样的插入是无意义的,插入失败!

以上图为例,如果要插入一个76

模拟实现

/*	template <class T>
	//二叉搜索的节点
	struct TreeNode {
		typedef TreeNode<T> Node;
		Node* _left;
		Node* _right;
		T  _val;
		//构造函数
		TreeNode(T val = T())
			:_left(nullptr),
			_right(nullptr),
			_val(val)
		{}
	};
*/

bool insert(T val) {
	if (_root == nullptr)
	{
		_root = new Node(val);
	}
	Node* child = _root;
	Node* parent = _root;
	while (child != nullptr) {
		parent = child;
		if (val < child->_val)	//val比根节点小,往左走
		{
			child = child->_left;
		}
		else if (val > child->_val)	//val比根节点大,往右走
		{
			child = child->_right;
		}
		else {
			return false;	//val与根节点相等,二叉搜索树中不许存在相同元素,所以不可插入,返回假
		}
	}
	if (parent->_val < val)	
		parent->_right = new Node(val);
	else if (parent->_val > val)	
		parent->_left = new Node(val);
	return true;	//插入成功
}

查找

查找与插入很类似,大于的话去右边找,小于的话去左边找,找到了返回节点,没找到返回nullptr

以找64为例

模拟实现

/*	template <class T>
	//二叉搜索的节点
	struct TreeNode {
		typedef TreeNode<T> Node;
		Node* _left;
		Node* _right;
		T  _val;
		//构造函数
		TreeNode(T val = T())
			:_left(nullptr),
			_right(nullptr),
			_val(val)
		{}
	};
*/

Node* find(T val) {
	Node* tmp = _root;
	while (tmp != nullptr) {
		if (val == tmp->_val)
			return tmp;	//找到啦
		else if (val > tmp->val) {
			tmp = tmp->_right;		//val大于该节点的值,去右子树找
		}
		else {
			tmp = tmp->_left;		//val小于该节点的值,去左子树找
		}
	}
	return nullptr;		//树里没有val,不存在该节点
}

删除

这边我们重画一个比较大一些的搜索二叉树以便于举例,数据很简单,1到16

首先,当删去8时,其余的所有节点都仍旧符合搜索二叉树的定义,删去其他叶子节点有相同的效果,同理,在删去2节点时,理论上来说仅有2的子树受到影响,搜索树的其他部分仍旧符合定义.

再以删去九号节点为例,由于二叉树的定义,删去九号节点后新节点的值必须满足除自身外,左子树都小于其,右子树都大于其.

由上图为例,对二叉树进行分析可得:除8外的左树<8<9<10<除10外的右树,也就是说:

左子树的右边的右边的右边的......的叶节点和右子树左边的左边的.......的叶节点最适合做根节点


特殊情况1:左子树没有右节点

使用左子树自身的根节点即可.


特殊情况2:没有左子树&&只有一个孩子节点

上图为删除节点4的示意图,可以看出,4号节点没有左子树,此时4号节点仅有一个子节点,直接让子节点代替自己即可


模拟实现

		bool earse(const T& val) {
			Node* parent = _root;
			Node* child = _root;
			while (child!= nullptr) {
				if (val > child->_val)
				{
					parent = child;
					child = child->_right;
				}
				else if (val < child->_val){
					parent = child;
					child = child->_left;
				}
				else   //相等
				{
					if (child->_left == nullptr)
					{
						if (child == _root)
							_root = child->_right;
						else {
							if (parent->_left == child)	parent->_left = child->_right;
							else if (parent->_right == child)	parent->_right = child->_right;
						}
						delete child;
					}
					else if (child->_right == nullptr) {
						if (child == _root)
							_root = child->_left;
						else {
							if (parent->_left == child)	parent->_left = child->_left;
							else if (parent->_right == child)	parent->_right = child->_left;
						}
						delete child;
					}
					else {
						Node* tmp = child->_left;
						Node* pp = tmp;

						while (tmp->_right) {
							pp = tmp;
							tmp = tmp->_right;
						}
						child->_val = tmp->_val;

						if (pp != tmp)
							pp->_right = tmp->_left;
						delete tmp;
					}
					return true;
				}
			}
			return false;
		}

高度(不重要)

	public:
		size_t height() {
			return _height(_root);
		}
	private:
		size_t _height(Node* root) {
			if (root == nullptr)
				return 0;
			else
				return _height(root->_left) + _height(root->_right) + 1;
		}

节点个数(不重要)

	public:
		size_t size() {
			return _size(_root);
		}
	private:
		size_t _size(Node* root) {
			if (root == nullptr)
				return 0;
			return _size(root->_left) + _size(root->_right) + 1;
		}

打印(不重要)

void print() {
	std::queue<Node*> q1,q2;
	q1.push(_root);
	while (!q1.empty() || !q2.empty()) {
		while (!q1.empty()) {
			if (q1.front() == nullptr)
				std::cout << '#' << ' ';
			else {
				q2.push(q1.front()->_left);
				q2.push(q1.front()->_right);
				std::cout << q1.front()->_val << ' ';
			}
			q1.pop();
		}
		std::swap(q1,q2);
		std::cout << std::endl;
	}

}

搜索二叉树模拟实现

#include <queue>
#include <iostream>
namespace SearchTree {
	template <class T>
	//二叉搜索的节点
	struct TreeNode {
		typedef TreeNode<T> Node;
		Node* _left;
		Node* _right;
		T  _val;
		//构造函数
		TreeNode(T val = T())
			:_left(nullptr),
			_right(nullptr),
			_val(val)
		{}
	};
	template <class T>
	class SearchTree {
		typedef TreeNode<T> Node;
		Node* _root;	//根节点
	public:
		SearchTree():_root(nullptr){}		//构造函数
		bool insert(T val) {
			if (_root == nullptr)
			{
				_root = new Node(val);
			}
			Node* child = _root;
			Node* parent = _root;
			while (child != nullptr) {
				parent = child;
				if (val < child->_val)	//val比根节点小,往左走
				{
					child = child->_left;
				}
				else if (val > child->_val)	//val比根节点大,往右走
				{
					child = child->_right;
				}
				else {
					return false;	//val与根节点相等,二叉搜索树中不许存在相同元素,所以不可插入,返回假
				}
			}
			if (parent->_val < val)	
				parent->_right = new Node(val);
			else if (parent->_val > val)	
				parent->_left = new Node(val);
			return true;	//插入成功
		}
		bool earse(const T& val) {
			Node* parent = _root;
			Node* child = _root;
			while (child!= nullptr) {
				if (val > child->_val)
				{
					parent = child;
					child = child->_right;
				}
				else if (val < child->_val){
					parent = child;
					child = child->_left;
				}
				else   //相等
				{
					if (child->_left == nullptr)
					{
						if (child == _root)
							_root = child->_right;
						else {
							if (parent->_left == child)	parent->_left = child->_right;
							else if (parent->_right == child)	parent->_right = child->_right;
						}
						delete child;
					}
					else if (child->_right == nullptr) {
						if (child == _root)
							_root = child->_left;
						else {
							if (parent->_left == child)	parent->_left = child->_left;
							else if (parent->_right == child)	parent->_right = child->_left;
						}
						delete child;
					}
					else {
						Node* tmp = child->_left;
						Node* pp = tmp;

						while (tmp->_right) {
							pp = tmp;
							tmp = tmp->_right;
						}
						child->_val = tmp->_val;

						if (pp != tmp)
							pp->_right = tmp->_left;
						delete tmp;
					}
					return true;
				}
			}
			return false;
		}
		Node* find(T val) {
			Node* tmp = _root;
			while (tmp != nullptr) {
				if (val == tmp->_val)
					return tmp;	//找到啦
				else if (val > tmp->val) {
					tmp = tmp->_right;		//val大于该节点的值,去右子树找
				}
				else {
					tmp = tmp->_left;		//val小于该节点的值,去左子树找
				}
			}
			return nullptr;		//树里没有val,不存在该节点
		}
		void print() {
			std::queue<Node*> q1,q2;
			q1.push(_root);
			while (!q1.empty() || !q2.empty()) {
				while (!q1.empty()) {
					if (q1.front() == nullptr)
						std::cout << '#' << ' ';
					else {
						q2.push(q1.front()->_left);
						q2.push(q1.front()->_right);
						std::cout << q1.front()->_val << ' ';
					}
					q1.pop();
				}
				std::swap(q1,q2);
				std::cout << std::endl;
			}

		}
		size_t size() {
			return _size(_root);
		}
		size_t height() {
			return _height(_root);
		}
	private:
		size_t _size(Node* root) {
			if (root == nullptr)
				return 0;
			return _size(root->_left) + _size(root->_right) + 1;
		}
		size_t _height(Node* root) {
			if (root == nullptr)
				return 0;
			else
				return std::max(_height(root->_left) , _height(root->_right)) + 1;
		}
	};
}

对搜索二叉树的分析及AVL树红黑树的优势

参考我在前文查找处的分析可得,查找的时间复杂度与树的高度,空间复杂度恒为o(1),当插入从a到b的值是,优先输入[a,b]中间的值,后输入接近a,b的极端值,此时树较为平衡,查找的时间复杂度接近o(logn),而当数据有序的进行插入时,树相当于一个链表,时间复杂度较高,为(o(n))

举个例子,当把数据[1,2,3,4,5,6,7]插入二叉树时

以[4,2,1,3,6,5,7]插入时,如果我们要查找7,需要进行三次判断即可,

以[1,2,3,4,5,6,7]插入时,如果我们要查找7,需要进行七次判断!!!!时间复杂度为o(n),再加上额外的空间开销,还不如直接在原数组中查找

如果有一种改进的插入方式可以在插入时,依据原树的高度差进行动态的重构树结构,便可大大加快查找速度

附:AVL树模拟实现(供参考)

#include <queue>
#include <iostream>
#include <assert.h>
namespace AVL {

	template<class T>
	struct AVLTreeNode
	{
		AVLTreeNode(const T& data = T())
			: _pLeft(nullptr)
			, _pRight(nullptr)
			, _pParent(nullptr)
			, _data(data)
			, _bf(0)
		{}

		AVLTreeNode<T>* _pLeft;
		AVLTreeNode<T>* _pRight;
		AVLTreeNode<T>* _pParent;
		T _data;
		int _bf;   // 节点的平衡因子
	};


	// AVL: 二叉搜索树 + 平衡因子的限制
	template<class T>
	class AVLTree
	{
		typedef AVLTreeNode<T> Node;
	public:
		AVLTree()
			: _pRoot(nullptr)
		{}
		void print() {
			std::queue<Node*>q1;
			std::queue<Node*>q2;
			q1.push(_pRoot);
			while (!q1.empty() || !q2.empty()) {
				while (!q1.empty())
				{
					if (q1.front() != nullptr) {
						std::cout << q1.front()->_data << ' ';
						q2.push(q1.front()->_pLeft);
						q2.push(q1.front()->_pRight);
					}
					else
						std::cout << '#' << ' ';
					q1.pop();
				}
				swap(q1, q2);
				std::cout << std::endl;
			}
		}
		// 在AVL树中插入值为data的节点
		bool Insert(const T& data) {
			Node* node = new Node(data);
			if (_pRoot == nullptr) {
				_pRoot = node;
				return true;
			}
			Node* parent = _pRoot,*child = _pRoot;
			while (child) {
				parent = child;
				if (child->_data > data)	child = child->_pLeft;
				else if (child->_data < data)	child = child->_pRight;
				else return false;
			}
			if (parent->_data < data)
			{
				parent->_pRight = node;
			}
			else
			{
				parent->_pLeft = node;
			}
			node->_pParent = parent;
			while (parent) {
				if (node == parent->_pLeft)
					parent->_bf--;
				else
					parent->_bf++;
				if (parent->_bf == 0)
					break;
				else if (parent->_bf == 1 || parent->_bf == -1)
					node = parent, parent = parent->_pParent;
				else {		//出问题了
					if (parent->_bf == -2 && node->_bf == -1) {
						RotateR(node);
					}
					else if (parent->_bf == -2 && node->_bf == 1)
					{
						RotateLR(node);
					}
					else if (parent->_bf == 2 && node->_bf == 1) {
						RotateL(parent);
					}
					else if (parent->_bf == -2 && node->_bf == -1) {
						RotateRL(node);
					}
					else {
						//   树损坏
					}
				}
			}
			return true;
		}

		// AVL树的验证
		bool IsAVLTree()
		{
			return _IsAVLTree(_pRoot);
		}

	private:
		// 根据AVL树的概念验证pRoot是否为有效的AVL树
		bool _IsAVLTree(Node* pRoot) {
			std::queue<Node*> q;
			q.push(pRoot);
			while (!q.empty()) {
				if (q.front() == nullptr)
				{
					q.pop();
					continue;
				}
				if (q.front()->_bf >= 2 || q.front()->_bf <= -2)
				{
					return false;
				}
				else {
					q.push(q.front()->_pLeft);
					q.push(q.front()->_pRight);
					q.pop();
				}
			}
			return true;
		}
		size_t _Height(Node* pRoot) {
			size_t h = 0;
			std::queue<Node*> q1;
			std::queue<Node*> q2;
			q2.push(pRoot);
			while (!q1.empty()&&!q2.empty()) {
				while (!q1.empty())
				{
					if (q1.front() != nullptr)
					{
						q2.push(q1.front()->_pLeft);
						q2.push(q1.front()->_pRight);
					}
					q1.pop();
				}
				std::swap(q1, q2);
				h++;
			}
			q1.empty();
			q2.empty();
			return h - 1;
		}
		// 左单旋
		void RotateL(Node* pParent) {
			//新的头节点
			Node* new_Parent = pParent->_pRight;
			//旧头结点的父节点
			Node* grand = pParent->_pParent;
			//修改pParent的父节点
			if (grand != nullptr)
			{
				if (grand->_pLeft == pParent)
					grand->_pLeft = new_Parent;
				else
					grand->_pRight = new_Parent;
			}
			else {
				_pRoot = new_Parent;
			}
			// 修改pParent节点
			pParent->_pParent = new_Parent;
			pParent->_pRight = new_Parent->_pLeft;

			//修改new_Parent节点
			if(new_Parent->_pLeft!=nullptr)
				new_Parent->_pLeft->_pParent = pParent;
			new_Parent->_pLeft = pParent;
			new_Parent->_pParent = grand;
			pParent->_pParent = new_Parent;

			pParent->_bf = new_Parent->_bf = 0;
		}
		// 右单旋
		void RotateR(Node* pParent) {
			//新的头节点
			Node* new_Parent = pParent->_pLeft;
			//旧头结点的父节点
			Node* grand = pParent->_pParent;
			//修改pParent的父节点
			if (grand != nullptr)
			{
				if (grand->_pLeft == pParent)
					grand->_pLeft = new_Parent;
				else
					grand->_pRight = new_Parent;
			}
			else {
				_pRoot = new_Parent;
			}
			// 修改pParent节点
			pParent->_pParent = new_Parent;
			pParent->_pLeft = new_Parent->_pRight;

			//修改new_Parent节点
			if(new_Parent->_pRight!=nullptr)
				new_Parent->_pRight->_pParent = pParent;
			new_Parent->_pRight = pParent;
			new_Parent->_pParent = grand;
			pParent->_pParent = new_Parent;

			pParent->_bf = new_Parent->_bf = 0;
		}
		// 右左双旋
		void RotateRL(Node* pParent) {
			RotateR(pParent);
			RotateL(pParent->_pParent->_pParent);
		}
		// 左右双旋
		void RotateLR(Node* pParent) {
			RotateL(pParent);
			RotateR(pParent->_pParent->_pParent);
		}
	protected:
		Node* _pRoot;

	};
}

结语

截止至结语,本文已有接近1万字,制作不易可以留个免费的赞吗

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值