二叉搜索树(BST)分析及实现


二叉搜索树是AVL树和红黑树的基础, 学好二叉搜索树,有助于更好的理解map和set的特性。

概念

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

  1. 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值。
  2. 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值 。
  3. 它的左右子树也分别为二叉搜索树。

如图即为一颗二叉搜索树:
在这里插入图片描述
如果按中序遍历的话应该为递增顺序: 9,17, 23, 45, 5 3, 65, 78, 81, 87, 94

定义二叉树节点:

class BSTNode
{
	friend class BSTree<_Ty>;
public:
	BSTNode() : data(_Ty()), leftChild(nullptr), rightChild(nullptr)
	{}
	BSTNode(_Ty val, BSTNode* left = nullptr, BSTNode* right = nullptr)
		: data(val), leftChild(left), rightChild(right)
	{}
	~BSTNode()
	{}
private:
	_Ty data;
	BSTNode* leftChild;
	BSTNode* rightChild;
};

定义二叉树:

class BSTree
{
public:
	BSTree():root(nullptr)
	{}
	BSTree(vector<_Ty> & num)  : root(nullptr)
	{
		for (const auto e : num) {
			Insert1(root, e);
			//Insert2(root,e);
		}
	}
	~BSTree()
	{}

	/// <summary>
	/// 核心功能 增删
	/// <returns>
	bool Insert(const _Ty& x)
	{
		return Insert2(root, x); 
		//return Insert1(root, x);
	}

	bool Remove(const _Ty& key)
	{
		return Remove1(root, key);
		//return Remove2(root, key);
	}
	
private:
	BSTNode<_Ty>* root;

};

构造二叉树时需要用逐个元素插入;而插入就要先查找到该插入的位置;

查找

在这里插入图片描述

BSTNode<_Ty>* Search(BSTNode<_Ty>* t, const _Ty& key)const
	{
		if (t == nullptr)
			return t;
		if (key < t->data)
			return Search(t->leftChild, key);
		else if (key > t->data)
			return Search(t->rightChild, key);
		return t;
	}

插入

在这里插入图片描述

bool Insert1(BSTNode<_Ty>*& t, int x)  //递归
	{
		if (t == nullptr) {
			t = new BSTNode<_Ty>(x);
			return true;
		}
		
		if (x > t->data) {
			return  Insert1(t->rightChild, x);
		}
		if (x < t->data) {
			return  Insert1(t->leftChild, x);
		}
		return false;
	}
	bool Insert2(BSTNode<_Ty>* &t, const _Ty& x)  //非递归  需要找到当前节点的父节点
	{  
		if (t == nullptr) {
			t = new BSTNode<_Ty>(x);
			return true;
		}
		BSTNode<_Ty>* p = t;    //当前节点
		BSTNode<_Ty>* pre = t;  //父节点
		while (p) {             //比较大小走到分支尽头结束,此时p=nullptr
			pre = p;
			if (x > t->data) {
				p = p->rightChild;
			}
			else if (x < t->data) {
				p = p->leftChild;
			}
		}	
		//然后比较x和pre->data的大小决定插入的是左子树还是右子树
		if (x > pre->data) {
			pre->rightChild= new BSTNode<_Ty>(x);
		}
		else {
			pre->leftChild = new BSTNode<_Ty>(x);
		}
		return true;
	}

删除

删除操作比较复杂一点:
首先查找元素是否在二叉搜索树中,如果不存在,则返回, 否则要删除的结点可能分下面四种情况:

  1. 要删除的结点只有左孩子结点。
  2. 要删除的结点只有右孩子结点 。
  3. 要删除的结点有左、右孩子结点
  4. 要删除的结点无左、右孩子结点

前三种情况删除节点后需要调整:
在这里插入图片描述
考虑完前三种情况就剩下第四种情况,第四种情况可以包含到1,2 情况中。

bool Remove1(BSTNode<_Ty>* &t, const _Ty& key)  //递归
	{
		if (t == nullptr)
			return false;
		if (key < t->data)
			return Remove1(t->leftChild,key);
		else if (key > t->data)
			return Remove1(t->rightChild, key);
		else{  //找到了则删除   
			
			if (t->leftChild && t->rightChild) {  //左右子树都不空
				BSTNode<_Ty> * q = t->rightChild;
				while (q->leftChild)
					q = q->leftChild;   //找右子树最小值
				t->data = q->data;      
				Remove1(t->rightChild, t->data);   //删除刚才被置换的节点
			}
			else { // 三种情况:左子树空  或者右子树空  或者左右子树都为空
				BSTNode<_Ty>*  p = t;
				if (t->leftChild != nullptr)   //左树不空
					t = t->leftChild;
				else    //右树不空
					t = t->rightChild;
				delete p;
			}			
		}
		return true;
	}

	bool Remove2(BSTNode<_Ty>*& t, const _Ty& key)  //非递归
	{
		if (t == nullptr)
			return false;
		BSTNode<_Ty>* cur = Search(key);
		if (cur == nullptr)
			return false;

		if (cur->leftChild && cur->rightChild) {  //左右子树都存在 可选择替换右子树的最小值或左子树的最大值			
			BSTNode<_Ty>* q = cur->leftChild;
			while (q->rightChild != nullptr)
				q = q->rightChild;
			cur->data = q->data;
			cur = q;
		}

		BSTNode<_Ty>* p = Parent(t,cur);   //获取父节点
		if (p == nullptr) //删除根节点  此时只有左树或者右树 或者都无
		{
			if (cur->leftChild != nullptr)
				cur = cur->leftChild;
			else
				cur = cur->rightChild;
		}
		else {	//非根节点
			if (p->leftChild==cur) { //cur为左树 且cur只有一颗子树
				if (cur->leftChild == nullptr)
					p->leftChild = cur->rightChild;
				else
					p->leftChild = cur->leftChild;
			}
			else if (p->rightChild== cur) {//cur为右树 且cur只有一颗子树
				if (cur->leftChild == nullptr)
					p->rightChild = cur->rightChild;
				else
					p->rightChild = cur->leftChild;
			}
			
			
		}	
		delete cur;
		return true;
	}

二叉搜索树的性能

插入和删除操作都必须先查找,查找效率代表了二叉搜索树中各个操作的性能。
对有n个结点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是结点在二叉搜索树的深度的函数,即结点越深,则比较次数越多。
但对于同一个关键码集合,如果各关键码插入的次序不同,可能得到不同结构的二叉搜索树:
在这里插入图片描述
最优情况下,二叉搜索树为完全二叉树,其平均比较次数为:log 2 N
最差情况下,二叉搜索树退化为单支树,其平均比较次数为:N/2 。 优化方案----->可使用平衡二叉搜索树

完整代码

#include <iostream>
#include <vector>
using namespace std;

template<class _Ty>
class BSTree;

template<class _Ty>
class BSTNode
{
	friend class BSTree<_Ty>;
public:
	BSTNode() : data(_Ty()), leftChild(nullptr), rightChild(nullptr)
	{}
	BSTNode(_Ty val, BSTNode* left = nullptr, BSTNode* right = nullptr)
		: data(val), leftChild(left), rightChild(right)
	{}
	~BSTNode()
	{}
private:
	_Ty data;
	BSTNode* leftChild;
	BSTNode* rightChild;
};

template<class _Ty>
class BSTree
{
public:
	BSTree():root(nullptr)
	{}
	BSTree(vector<_Ty> & num)  : root(nullptr)
	{
		for (const auto e : num) {
			Insert1(root, e);
			//Insert2(root,e);
		}
	}
	~BSTree()
	{}
	/// 核心功能 增删
	bool Insert(const _Ty& x)
	{
		return Insert2(root, x); 
		//return Insert1(root, x);
	}

	bool Remove(const _Ty& key)
	{
		return Remove1(root, key);
		//return Remove2(root, key);
	}
	

	/// 其它功能
public:
	void Order()const   //中序遍历 方便查看结果
	{
		Order(root);
	}

	BSTNode<_Ty>* Min()const   //求树最小值
	{
		return Min(root);
	}
	BSTNode<_Ty>* Max()const  //求树最大值
	{
		return Max(root);
	}
	
	BSTNode<_Ty>* Parent(BSTNode<_Ty>* key)const   //寻找节点的父节点
	{
		return Parent(root, key);
	}
	BSTNode<_Ty>* Search(const _Ty& key)const
	{
		return Search(root, key);
	}
	

protected:
	bool Insert1(BSTNode<_Ty>*& t, int x)  //递归
	{
		if (t == nullptr) {
			t = new BSTNode<_Ty>(x);
			return true;
		}
		
		if (x > t->data) {
			return  Insert1(t->rightChild, x);
		}
		if (x < t->data) {
			return  Insert1(t->leftChild, x);
		}
		return false;
	}
	bool Insert2(BSTNode<_Ty>* &t, const _Ty& x)  //非递归  需要找到当前节点的父节点
	{  
		if (t == nullptr) {
			t = new BSTNode<_Ty>(x);
			return true;
		}
		BSTNode<_Ty>* p = t;    //当前节点
		BSTNode<_Ty>* pre = t;  //父节点
		while (p) {             //比较大小走到分支尽头结束,此时p=nullptr
			pre = p;
			if (x > t->data) {
				p = p->rightChild;
			}
			else if (x < t->data) {
				p = p->leftChild;
			}
		}	
		//然后比较x和pre->data的大小决定插入的是左子树还是右子树
		if (x > pre->data) {
			pre->rightChild= new BSTNode<_Ty>(x);
		}
		else {
			pre->leftChild = new BSTNode<_Ty>(x);
		}
		return true;
	}

	bool Remove1(BSTNode<_Ty>* &t, const _Ty& key)  //递归
	{
		if (t == nullptr)
			return false;
		if (key < t->data)
			return Remove1(t->leftChild,key);
		else if (key < t->data)
			return Remove1(t->rightChild, key);
		else{  //找到了则删除   
			
			if (t->leftChild && t->rightChild) {
				BSTNode<_Ty> * q = t->rightChild;
				while (q->leftChild)
					q = q->leftChild;
				t->data = q->data;
				Remove1(t->rightChild, t->data);
			}
			else {
				BSTNode<_Ty>*  p = t;
				if (t->leftChild != nullptr)
					t = t->leftChild;
				else
					t = t->rightChild;
				delete p;
			}			
		}
		return true;
	}

	bool Remove2(BSTNode<_Ty>*& t, const _Ty& key)
	{
		if (t == nullptr)
			return false;
		BSTNode<_Ty>* cur = Search(key);
		if (cur == nullptr)
			return false;

		if (cur->leftChild && cur->rightChild) {  //左右子树都存在 可选择替换右子树的最小值或左子树的最大值			
			BSTNode<_Ty>* q = cur->leftChild;
			while (q->rightChild != nullptr)
				q = q->rightChild;
			cur->data = q->data;
			cur = q;
		}

		BSTNode<_Ty>* p = Parent(t,cur);
		if (p == nullptr) //删除根节点  此时只有左树或者右树 或者都无
		{
			if (cur->leftChild != nullptr)
				cur = cur->leftChild;
			else
				cur = cur->rightChild;
		}
		else {	//非根节点
			if (p->leftChild==cur) { //cur为左树 且cur只有一颗子树
				if (cur->leftChild == nullptr)
					p->leftChild = cur->rightChild;
				else
					p->leftChild = cur->leftChild;
			}
			else if (p->rightChild== cur) {//cur为右树 且cur只有一颗子树
				if (cur->leftChild == nullptr)
					p->rightChild = cur->rightChild;
				else
					p->rightChild = cur->leftChild;
			}
			
			
		}	
		delete cur;
		return true;
	}

protected:

	void Order(BSTNode<_Ty>* t)const   //中序遍历 方便查看结果
	{
		if (t == nullptr) {
			return;
		}
		Order(t->leftChild);
		printf("%d  ", t->data);
		Order(t->rightChild);
	}

	BSTNode<_Ty>* Min(BSTNode<_Ty>*&t)
	{
		while (t&&t->leftChild != nullptr) {   //t==nullptr时返回nullptr
			t = t->leftChild;
		}
		return t;
	}

	BSTNode<_Ty>* Max(BSTNode<_Ty>* t)const
	{
		while (t && t->rightChild != nullptr)
			t = t->rightChild;
		return t;
	}

	BSTNode<_Ty>* Parent(BSTNode<_Ty>* t, BSTNode<_Ty>* key)const
	{
		if (t == nullptr || key == t)
			return nullptr;
		if (key == t->leftChild || key == t->rightChild)
			return t;
		if (key->data < t->data)
			return Parent(t->leftChild, key);
		else if (key->data > t->data)
			return Parent(t->rightChild, key);
	}

	BSTNode<_Ty>* Search(BSTNode<_Ty>* t, const _Ty& key)const
	{
		if (t == nullptr)
			return t;
		if (key < t->data)
			return Search(t->leftChild, key);
		else if (key > t->data)
			return Search(t->rightChild, key);
		return t;
	}

private:
	BSTNode<_Ty>* root;

};

int main()
{
	vector<int>  v1{53,78,17,45,23,9,65,87,81,94 };
	BSTree<int> b1(v1);
	b1.Insert(88);
	b1.Order();
	cout << endl;
	b1.Remove(17);
	b1.Remove(53);

	b1.Order();
	return 0;
}

在这里插入图片描述

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
以下是Java实现二叉搜索树的基本步骤和代码示例: 1.定义二叉搜索树节点类 ```java class TreeNode { int val; TreeNode left; TreeNode right; TreeNode(int x) { val = x; } } ``` 2.定义二叉搜索树类 ```java class BST { private TreeNode root; public BST() { root = null; } // 向二叉搜索树中插入元素 public void insert(int val) { root = insert(root, val); } private TreeNode insert(TreeNode node, int val) { if (node == null) { return new TreeNode(val); } if (val < node.val) { node.left = insert(node.left, val); } else if (val > node.val) { node.right = insert(node.right, val); } return node; } // 在二叉搜索树中查找元素 public boolean search(int val) { return search(root, val); } private boolean search(TreeNode node, int val) { if (node == null) { return false; } if (val == node.val) { return true; } else if (val < node.val) { return search(node.left, val); } else { return search(node.right, val); } } // 删除二叉搜索树中的元素 public void delete(int val) { root = delete(root, val); } private TreeNode delete(TreeNode node, int val) { if (node == null) { return null; } if (val < node.val) { node.left = delete(node.left, val); } else if (val > node.val) { node.right = delete(node.right, val); } else { if (node.left == null) { return node.right; } else if (node.right == null) { return node.left; } TreeNode minNode = findMin(node.right); node.val = minNode.val; node.right = delete(node.right, node.val); } return node; } private TreeNode findMin(TreeNode node) { while (node.left != null) { node = node.left; } return node; } } ``` 3.测试代码 ```java public class TestBST { public static void main(String[] args) { BST bst = new BST(); bst.insert(5); bst.insert(3); bst.insert(7); bst.insert(1); bst.insert(9); System.out.println(bst.search(3)); // 输出:true bst.delete(3); System.out.println(bst.search(3)); // 输出:false } } ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值