C++笔记归纳12:二叉搜索树


二叉搜索树

目录

二叉搜索树

一、二叉搜索树的概念

二、二叉搜索树的性能分析

三、二叉搜索树的模拟实现

3.1.二叉搜索树的节点定义

3.2.二叉搜索树的插入

3.3.二叉搜索树的查找

3.4.二叉搜索树的中序遍历

3.5.二叉搜索树的删除

3.6.二叉搜索树的总代码

四、二叉搜索树的使用场景

4.1.key搜索场景

4.2.key/value搜索场景


一、二叉搜索树的概念

左子树不为空,左子树上所有节点的值小于等于根节点的值

右子树不为空,右子树上所有节点的值大于等于根节点的值

(它的左右子树也分别为二叉搜索树)

二叉搜索树中可以支持插入相等的值

也可以不支持插入相等的值

**map/set:**不支持相等

**multimap/multiset:**支持相等

二、二叉搜索树的性能分析

**最优情况:**接近完全二叉树,高度:log(2)N

**最坏情况:**退化成为单枝树,高度:N/2

**时间复杂度:**O(N)

**二叉树的变形:**AVL树、红黑树、B树系列

三、二叉搜索树的模拟实现

3.1.二叉搜索树的节点定义

cpp 复制代码
//定义节点模板
template < class K>
struct BSTNode
{
	K _key;
	BSTNode<K>*_left;
	BSTNode<K>*_right;

	BSTNode(const K & key)
		:_key(key)
		, _left(nullptr)
		, _right(nullptr)
	{}
};

3.2.二叉搜索树的插入

cpp 复制代码
bool Insert(const K& key)
{
	if (_root == nullptr)
	{
		_root = new Node(key);
		return true;
	}

	Node* parent = nullptr;
	Node* cur = _root;
	while (cur)
	{
		if (cur->_key < key)
		{
			parent = cur;
			cur = cur->_right;
		}
		else if (cur->_key > 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.3.二叉搜索树的查找

cpp 复制代码
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;
}

3.4.二叉搜索树的中序遍历

cpp 复制代码
void _InOrder(Node* root)
{
	if (root == nullptr)
	{
		return;
	}
	 _InOrder(root->_left);
	cout << root->_key << " ";
	_InOrder(root->_right);
}

3.5.二叉搜索树的删除

首先查找元素是否在二叉搜索树中,如果不存在,则返回false

查找元素要分四种情况处理

**第一种:**要删除节点N的左右孩子均为空

把节点N的父亲对应孩子的指针指向空,直接删除N节点

**第二种:**要删除节点N的左孩子为空,右孩子节点不为空

把节点N的父亲对应孩子指针指向N的右孩子,直接删除N节点

**第三种:**要删除节点N的右孩子为空,左孩子节点不为空

把节点N的父亲对应孩子指针指向N的左孩子,直接删除N节点

**第四种:**要删除节点N的左右孩子节点均不为空

替代法

找N左子树的最大节点R(最右节点)

找N右子树的最小节点R(最左节点)

这两个节点任意一个放到N的位置

cpp 复制代码
bool Erase(const K& key)
{
	Node * parent = nullptr;
	Node * cur = _root;
	while (cur)
	{
		if (cur->_key < key)
		{
			parent = cur;
			cur = cur->_right;
		}
		else if (cur->_key > key)
		{
			parent = cur;
			cur = cur->_left;
		}
		else
		{
			//有0或1个孩子
			//删除情况1、2、3均可以直接删除
			//改变父亲对应孩子指针指向即可

			//左为空
			if (cur->_left == nullptr)
			{
				//判断是否是删除根
				//parent为空
				//if(cur == _root)
				if (parent == nullptr)
				{
					_root = cur->_right;
				}
				else
				{
					if (parent->_left == cur)
					{
						parent->_left = cur->_right;
					}
					else
					{
						parent->_right = cur->_right;
					}
				}
				delete cur;
				return true;
			}
			else if (cur->_right == nullptr)//右为空
			{
				if (parent == nullptr)
				{
					_root = cur->_left;
				}
				else
				{
					if (parent->_left == cur)
					{
						parent->_left = cur->_left;
					}
					else
					{
						parent->_right = cur->_left;
					}
				}
				delete cur;
				return true;
			}
			else
			{
				//有2个孩子的情况
				//删除情况4
				//假设这里我们取右子树的最小结点作为替代结点去删除
				//这里尤其要注意右子树的根就是最小情况的情况的处理
				//一定要把cur给replaceParent,否会报错
				Node * replaceParent = cur;
				Node * replace = cur->_right;
				while (replace->_left)
				{
					replaceParent = replace;
					replace = replace->_left;
				}
				cur->_key = replace->_key;

				if (replaceParent->_left == replace)
				{
					replaceParent->_left = replace->_right;
				}
				else
				{
					replaceParent->_right = replace->_right;
				}
				delete replace;
				return true;
			}
		}
	}
	return false;
}

3.6.二叉搜索树的总代码

cpp 复制代码
#pragma once

//定义节点模板
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>
class BSTree
{
	//typedef BSTNode<K> Node;
	using Node = BSTNode<K>;
public:
	bool Insert(const K& key)
	{
		if (_root == nullptr)
		{
			_root = new Node(key);
			return true;
		}
		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_key < key)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_key > 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;
	}

	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;
	}
	bool Erase(const K& key)
	{
		Node * parent = nullptr;
		Node * cur = _root;
		while (cur)
		{
			if (cur->_key < key)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_key > key)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				//有0或1个孩子
				//删除情况1、2、3均可以直接删除
				//改变父亲对应孩子指针指向即可

				//左为空
				if (cur->_left == nullptr)
				{
					//判断是否是删除根
					//parent为空
					//if(cur == _root)
					if (parent == nullptr)
					{
						_root = cur->_right;
					}
					else
					{
						if (parent->_left == cur)
						{
							parent->_left = cur->_right;
						}
						else
						{
							parent->_right = cur->_right;
						}
					}
					delete cur;
					return true;
				}
				else if (cur->_right == nullptr)//右为空
				{
					if (parent == nullptr)
					{
						_root = cur->_left;
					}
					else
					{
						if (parent->_left == cur)
						{
							parent->_left = cur->_left;
						}
						else
						{
							parent->_right = cur->_left;
						}
					}
					delete cur;
					return true;
				}
				else
				{
					//有2个孩子的情况
					//删除情况4
					//假设这里我们取右子树的最小结点作为替代结点去删除
					//这里尤其要注意右子树的根就是最小情况的情况的处理
					//一定要把cur给replaceParent,否会报错
					Node * replaceParent = cur;
					Node * replace = cur->_right;
					while (replace->_left)
					{
						replaceParent = replace;
						replace = replace->_left;
					}
					cur->_key = replace->_key;

					if (replaceParent->_left == replace)
					{
						replaceParent->_left = replace->_right;
					}
					else
					{
						replaceParent->_right = replace->_right;
					}
					delete replace;
					return true;
				}
			}
		}
		return false;
	}
	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}
private:
	void _InOrder(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}
		 _InOrder(root->_left);
		cout << root->_key << " ";
		_InOrder(root->_right);
	}
private:
	Node* _root = nullptr;
};

四、二叉搜索树的使用场景

4.1.key搜索场景(set)

只有key作为关键码,结构中只需要存储key即可

(判断在不在)

**关键码:**需要搜索到的值

key的搜索场景实现的二叉搜索树支持增删查,但不支持修改

(修改key会破坏树的结构)

场景:

检查一篇英文文章单词拼写是否正确

将词库中的所有单词放入二叉搜索树,读取文章中的单词

查找是否在二叉搜索树中,不在用红色波浪线标红提示

4.2.key/value搜索场景(map)

每一个关键码key,都有与之对应的值value

value可以是任意类型的对象

树的结构中(节点)除了需要存储key还有存储对应的value

增/删/查还是以key为关键字走二叉搜索树的规则进行比较

可以快速查到key对应的value,这种二叉搜索树支持修改

但只支持修改value,因为修改key会破坏搜索树的结构

场景:

简单中英文互译字典,树的结构中节点存储key(英文)和value(中文)

搜索时输入英文,则同时查找到了英文对应的中文

代码实现:

cpp 复制代码
#pragma once
#include <iostream>
using namespace std;

namespace key_value
{
	template <class K,class V>
	struct BSTNode
	{
		K _key;
		V _value;
		BSTNode<K,V>* _left;
		BSTNode<K,V>* _right;

		BSTNode(const K& key,const V& value)
			:_key(key)
			, _value(value)
			, _left(nullptr)
			, _right(nullptr)
		{}
	};

	template <class K,class V>
	class BSTree
	{
		using Node = BSTNode<K,V>;
	public:
		//强制生成构造
		BSTree() = default;
		BSTree(const BSTree<K,V>& t)
		{
			_root = Copy(t._root);
		}
		BSTree<K,V>& operator=(BSTree<K,V> t)
		{
			swap(_root, t._root);
			return *this;
		}
		~BSTree()
		{
			Destroy(_root);
			_root = nullptr;
		}
		bool Insert(const K& key,const V& value)
		{
			if (_root == nullptr)
			{
				_root = new Node(key,value);
				return true;
			}
			Node* parent = nullptr;
			Node* cur = _root;
			while (cur)
			{
				if (cur->_key < key)
				{
					parent = cur;
					cur = cur->_right;
				}
				else if (cur->_key > key)
				{
					parent = cur;
					cur = cur->_left;
				}
				else
				{
					return false;
				}
			}
			cur = new Node(key,value);
			if (parent->_key < key)
			{
				parent->_right = cur;
			}
			else
			{
				parent->_left = cur;
			}
			return true;
		}

		Node* 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 cur;
				}
			}
			return nullptr;
		}
		bool Erase(const K& key)
		{
			Node* parent = nullptr;
			Node* cur = _root;
			while (cur)
			{
				if (cur->_key < key)
				{
					parent = cur;
					cur = cur->_right;
				}
				else if (cur->_key > 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;
					}
					else if (cur->_right == nullptr)
					{
						if (parent == nullptr)
						{
							_root = cur->_left;
						}
						else
						{
							if (parent->_left == cur)
							{
								parent->_left = cur->_left;
							}
							else
							{
								parent->_right = cur->_left;
							}
						}
						delete cur;
						return true;
					}
					else
					{
						Node* replaceParent = cur;
						Node* replace = cur->_right;
						while (replace->_left)
						{
							replaceParent = replace;
							replace = replace->_left;
						}
						cur->_key = replace->_key;
						if (replaceParent->_left == replace)
						{
							replaceParent->_left = replace->_right;
						}
						else
						{
							replaceParent->_right = replace->_right;
						}
						delete replace;
						return true;
					}
				}
			}
			return false;
		}
		void InOrder()
		{
			_InOrder(_root);
			cout << endl;
		}
	private:
		void _InOrder(Node* root)
		{
			if (root == nullptr)
			{
				return;
			}
			_InOrder(root->_left);
			cout << root->_key << ":" << root->_value << endl;
			_InOrder(root->_right);
		}
		void Destroy(Node* root)
		{
			if (root == nullptr)
			{
				return;
			}
			Destroy(root->_left);
			Destroy(root->_right);
			delete root;
		}
		Node* Copy(Node* root)
		{
			if (root == nullptr)
			{
				return nullptr;
			}
			Node * newRoot = new Node(root->_key, root->_value);
			newRoot->_left = Copy(root->_left);
			newRoot->_right = Copy(root->_right);
			return newRoot;
		}
	private:
		Node* _root = nullptr;
	};
}

测试1:

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS 1
#include "BinarySearch.h"

int main()
{
	string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };
	key_value::BSTree<string, int> countTree;
	for (const auto& str : arr)
	{
		//先查找水果在不在搜索树中
		//不在,说明水果第一次出现,则插入<水果, 1>
		//在,则查找到的结点中水果对应的次数++
		//BSTreeNode<string, int>* ret = countTree.Find(str);
		auto ret = countTree.Find(str);
		if (ret == NULL)
		{
			countTree.Insert(str, 1);
		}
		else
		{
			ret->_value++;
		}
	}
	countTree.InOrder();
	return 0;
}

测试2:

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS 1
#include "BinarySearch.h"

int main()
{
	key_value::BSTree<string, string> dict;
	dict.Insert("left", "左边");
	dict.Insert("right", "右边");
	dict.Insert("insert", "插入");
	dict.Insert("string", "字符串");

	string str;
	while (cin >> str)
	{
		auto ret = dict.Find(str);
		if (ret)
		{
			cout << "->" << ret->_value << endl;
		}
		else
		{
			cout << "无此单词,请重新输入" << endl;
		}
	}
	return 0;
}
相关推荐
炎爆的土豆翔2 小时前
SIMD常见操作,结合样例一文理解
开发语言·c++·算法
仰泳的熊猫2 小时前
题目2305:蓝桥杯2019年第十届省赛真题-等差数列
数据结构·c++·算法·蓝桥杯
码界奇点2 小时前
基于.NET Core的CMS内容管理系统设计与实现
c++·毕业设计·.netcore·源代码管理
Geoking.2 小时前
【新手向】go语言最新下载及安装配置教程
开发语言·后端·golang
ん贤2 小时前
Go map 底层原理
算法·golang·map
游戏开发爱好者82 小时前
如何使用Instruments和Keymob进行Swift应用性能优化分析
开发语言·ios·性能优化·小程序·uni-app·iphone·swift
小温冲冲2 小时前
ReSharper 在 Visual Studio 中的详细配置指南
c++·ide·qt·visual studio
奔跑吧邓邓子2 小时前
Hash算法性能优化:从理论到实战的飞跃
算法·性能优化·哈希算法·hash·理论到实战
6+h2 小时前
【java IO】字节流详解
java·开发语言·python