C++——二叉树搜索树

前面写了初阶数据结构——二叉树;本文内容是来对它来进行结尾

目录

一概念

二实现

2.1查找

2.2插入 

2.3删除

完整源代码

三二叉树的应用 

 四二叉搜索树的性能分析

 五二叉搜索树相关的面试题


一概念

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

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

二实现

以下面的二叉搜索树为例:

2.1查找

a、从根开始比较,查找,比根大则往右边走查找,比根小则往左边走查找
b、走到到空,还没找到,这个值不存在。

bool find(const K& val)
{
	Node* cur = _root;
	while (cur)
	{
		if (cur->_key < val)
		{
			cur = cur->_right;
		}
		else if (cur->_key > val)
		{
			cur = cur->_left;
		}
		else
		{
			//相等
			return true;
		}
	}
	return false;
}

2.2插入 

a. 树为空,则直接新增节点,赋值给root指针
b. 树不空,按二叉搜索树性质查找插入位置,插入新节点

bool insert(const K& val)
{
	if (_root == nullptr)
	{
		_root = new Node(val);
		return true;
	}
	else
	{
		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_key < val)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_key > val)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				//相等
				return false;
			}
		}
		//判断要在parent的左还是右进行插入
		cur = new Node(val);
		if (parent->_key > val)
		{
			parent->_left = cur;
		}
		else
		{
			parent->_right = cur;
		}
		return true;
	}
}

2.3删除

a先通过二叉搜索树的性质找到节点的位置

b分析删除节点的左右孩子的情况:

无左右孩子节点(不考虑)

只有左孩子节点:删除之前把左孩子交给父亲节点

只有右孩子节点:删除之前把右孩子交给父亲节点

右孩子节点都有:有两种解决方法:

1找左节点的最大值的节点Max:Max的val与待删除的val进行交换;

2找右孩子的最小值的节点Min:Min的val与待删除的val进行交换;

以第二种为例来设计代码:

要注意对特殊情况的处理(删除根节点的情况):

 

特别要记录cur(删除节点)的父节点(cur在父节点的左边还是右边不清楚

bool erase(const K& val)
{
	if (_root == nullptr) return false;
	Node* parent = nullptr;
	Node* cur = _root;

	while (cur)
	{
		if (cur->_key < val)
		{
			parent = cur;
			cur = cur->_right;
		}
		else if (cur->_key > val)
		{
			parent = cur;
			cur = cur->_left;
		}
		else
		{
			//找到删除位置
			//右孩子为空
			if (cur->_right == nullptr)
			{
				//cur是根节点
				if (parent == nullptr) _root = cur->_left;
				//cur的左孩子交给parent
				else
				{
					if (parent->_left == cur) parent->_left = cur->_left;
					else if (parent->_right == cur) parent->_right = cur->_left;
				}
				delete cur;
			}
			//右孩子为空
			else if (cur->_left == nullptr)
			{
				//cur==_root
				if (parent == nullptr) _root = cur->_right;
				else
				{
					if (parent->_left == cur) parent->_left = cur->_right;
					else if (parent->_right == cur) parent->_right = cur->_right;
				}
				delete cur;
			}
			//都有
			else
			{
				//找到右节点的最小值进行替换删除(左节点的最大值)

				//要删除的可能是_root patent不能为nullptr
				Node* ParentRightMin = cur;
				Node* RightMin = cur->_right;
				while (RightMin->_left)
				{
					ParentRightMin = RightMin;
					RightMin = RightMin->_left;
				}

				swap(RightMin->_key, cur->_key);

				//RightMin的右子树交给ParentRightMin
				if (ParentRightMin->_right == RightMin)
				{
					ParentRightMin->_right = RightMin->_right;
				}
				else
				{
					ParentRightMin->_left = RightMin->_right;
				}

				delete RightMin;
			}
			return true;
		}
	}
	return false;
}

完整源代码


#pragma once
#include<iostream>
using namespace std;

namespace bit
{
	template<class K>
	struct Node
	{
		Node(const K& key = K())
			:_left(nullptr)
			, _right(nullptr)
			, _key(key)
		{}

		Node<K>* _left;
		Node<K>* _right;
		K _key;
	};
	template<class K>
	class BSTree
	{
		typedef Node<K> Node;
	public:
	
		bool insert(const K& val)
		{
			if (_root == nullptr)
			{
				_root = new Node(val);
				return true;
			}
			else
			{
				Node* parent = nullptr;
				Node* cur = _root;
				while (cur)
				{
					if (cur->_key < val)
					{
						parent = cur;
						cur = cur->_right;
					}
					else if (cur->_key > val)
					{
						parent = cur;
						cur = cur->_left;
					}
					else
					{
						//相等
						return false;
					}
				}
				//判断要在parent的左还是右进行插入
				cur = new Node(val);
				if (parent->_key > val)
				{
					parent->_left = cur;
				}
				else
				{
					parent->_right = cur;
				}
				return true;
			}
		}

		bool find(const K& val)
		{
			Node* cur = _root;
			while (cur)
			{
				if (cur->_key < val)
				{
					cur = cur->_right;
				}
				else if (cur->_key > val)
				{
					cur = cur->_left;
				}
				else
				{
					//相等
					return true;
				}
			}
			return false;
		}

		bool erase(const K& val)
		{
			if (_root == nullptr) return false;
			Node* parent = nullptr;
			Node* cur = _root;

			while (cur)
			{
				if (cur->_key < val)
				{
					parent = cur;
					cur = cur->_right;
				}
				else if (cur->_key > val)
				{
					parent = cur;
					cur = cur->_left;
				}
				else
				{
					//找到删除位置
					//右孩子为空
					if (cur->_right == nullptr)
					{
						//cur是根节点
						if (parent == nullptr) _root = cur->_left;
						//cur的左孩子交给parent
						else
						{
							if (parent->_left == cur) parent->_left = cur->_left;
							else if (parent->_right == cur) parent->_right = cur->_left;
						}
						delete cur;
					}
					//右孩子为空
					else if (cur->_left == nullptr)
					{
						//cur==_root
						if (parent == nullptr) _root = cur->_right;
						else
						{
							if (parent->_left == cur) parent->_left = cur->_right;
							else if (parent->_right == cur) parent->_right = cur->_right;
						}
						delete cur;
					}
					//都有
					else
					{
						//找到右节点的最小值进行替换删除(左节点的最大值)

						//要删除的可能是_root patent不能为nullptr
						Node* ParentRightMin = cur;
						Node* RightMin = cur->_right;
						while (RightMin->_left)
						{
							ParentRightMin = RightMin;
							RightMin = RightMin->_left;
						}

						swap(RightMin->_key, cur->_key);

						//RightMin的右子树交给ParentRightMin
						if (ParentRightMin->_right == RightMin)
						{
							ParentRightMin->_right = RightMin->_right;
						}
						else
						{
							ParentRightMin->_left = RightMin->_right;
						}

						delete RightMin;
					}
					return true;
				}
			}
			return false;
		}

		//进行套壳
		void _InOrder()
		{
			InOrder(_root);
			cout << endl;
		}

	private:
		void InOrder(const Node* _root)
		{
			if (_root == nullptr) return;
			InOrder(_root->_left);
			cout << _root->_key << " ";
			InOrder(_root->_right);
		}
	private:
		Node* _root=nullptr;
	};

	void Test1()
	{
		BSTree<int> sb;
		sb.insert(3);
		sb.insert(2);
		sb.insert(4);
		sb._InOrder();
		sb.erase(3);
		sb.erase(2);
		sb.erase(4);
		sb._InOrder();
	}
}

三二叉树的应用 

KV模型:

每一个关键码key,都有与之对应的值Value,即<Key, Value>的键值对。该种方式在现实生活中非常常见:
比如:英汉词典就是英文与中文的对应关系,通过英文可以快速找到与其对应的中文,英
文单词与其对应的中文<word, chinese>就构成一种键值对:

//对二叉搜索树进行改造:
template<class K,class V>
struct Node
{
	Node(const K& key = K(),const V& val=V())
		:_left(nullptr)
		, _right(nullptr)
		, _key(key)
		,_val(val)
	{}

	Node<K,V>* _left;
	Node<K,V>* _right;
	K _key;
	V _val;
};
template<class K,class V>
class BSTree
{
	typedef Node<K,V> Node;
public:

	bool Insert(const K& val,const K& valute)
	{
		if (_root == nullptr)
		{
			_root = new Node(val,valute);
			return true;
		}
		else
		{
			Node* parent = nullptr;
			Node* cur = _root;
			while (cur)
			{
				if (cur->_key < val)
				{
					parent = cur;
					cur = cur->_right;
				}
				else if (cur->_key > val)
				{
					parent = cur;
					cur = cur->_left;
				}
				else
				{
					//相等
					return false;
				}
			}
			//判断要在parent的左还是右进行插入
			if (parent->_key > val)
			{
				parent->_left = new Node(val,valute);
			}
			else
			{
				parent->_right = new Node(val,valute);
			}
			return true;
		}
	}

	Node* Find(const V& val)
	{
		Node* cur = _root;
		while (cur)
		{
			if (cur->_key < val)
			{
				cur = cur->_right;
			}
			else if (cur->_key > val)
			{
				cur = cur->_left;
			}
			else
			{
				//相等
				return cur;
			}
		}
		return nullptr;
	}
	void _InOrder()
	{
		InOrder(_root);
	}

private:
	void InOrder(const Node* _root)
	{
		if (_root == nullptr) return;
		InOrder(_root->_left);
		cout << _root->_key << endl;
		InOrder(_root->_right);
	}
	Node* _root = nullptr;
};

void Test1()
{
	// 输入单词,查找单词对应的中文翻译
	BSTree<string, string> dict;
	dict.Insert("string", "字符串");
	dict.Insert("tree", "树");
	dict.Insert("left", "左边、剩余");
	dict.Insert("right", "右边");
	dict.Insert("sort", "排序");
	// 插入词库中所有单词
	string str;
	while (cin >> str)
	{
		Node<string, string>* ret = dict.Find(str);
		if (ret == nullptr)
		{
			cout << "单词拼写错误,词库中没有这个单词:" << endl;
		}
		else
		{
			cout << "中文翻译:" << ret->_val << endl;
		}
	}
}

 四二叉搜索树的性能分析

插入和删除操作都必须先查找,查找效率代表了二叉搜索树中各个操作的性能。 

但二叉搜索树在不同的场景可能会有以下结构:

最优情况下,二叉搜索树为完全二叉树(或者接近完全二叉树),其平均比较次数为:long2N
最差情况下,二叉搜索树退化为单支树(或者类似单支),其平均比较次数为:N方

而在这种最差的情况下是有办法去去对它进行调整:将二叉树进行旋转,这个我们下文在说

 五二叉搜索树相关的面试题

 1. 二叉树创建字符串。oj链接
2. 二叉树的分层遍历1。oj链接
3. 二叉树的分层遍历2。oj链接
4. 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先 。oj链接
5. 二叉树搜索树转换成排序双向链表。oj链接
6. 根据一棵树的前序遍历与中序遍历构造二叉树。 oj链接
7. 根据一棵树的中序遍历与后序遍历构造二叉树。oj链接
8. 二叉树的前序遍历,非递归迭代实现 。oj链接
9. 二叉树中序遍历 ,非递归迭代实现。oj链接
10. 二叉树的后序遍历 ,非递归迭代实现。oj链接

以上便是我在学习二叉搜索树的相关内容,有错误欢迎在评论区指正,谢谢!! 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值