C++---二叉搜索树

目录

一、二叉搜索树的概念

二、二叉搜索树的操作

1、查找

2、插入

3、删除

三、二叉搜索树的实现

四、二叉搜索树的两种模型

1、K模型

2、KV模型

五、二叉搜索树性能分析


一、二叉搜索树的概念

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

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

二、二叉搜索树的操作

1、查找

基本思路:

  • 根节点为空,返回false
  • 根节点不为空,比较根节点和要找的值是否相等,相等返回true。
  • 根节点和要找的值不想等时,判断跟节点和要找的值的关系。
  • 如果根节点小于要找到的值,则在右子树找
  • 如果根节点大于要找的值,在左子树找。

2、插入

  • 树为空,直接插入

  • 树不为空,按照二叉搜索树的特点,找到插入的位置,插入节点。

3、删除

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

  •  要删除的结点无孩子结点:直接删除该节点
  • 要删除的结点只有左孩子结点:删除该结点且使被删除节点的双亲结点指向被删除节点的左孩子结点
  • 要删除的结点只有右孩子结点:删除该结点且使被删除节点的双亲结点指向被删除结点的右孩子结点
  • 要删除的结点有左、右孩子结点:在它的右子树中寻找中序下的第一个结点(关键码最小),用它的值填补到被删除节点中,即找到右子树的最左节点进行替换。要注意的是,有可能删除的节点的孩子节点就是最左节点。

三、二叉搜索树的实现

#pragma once
#include<iostream>

template<class k>
struct BSTNode
{
	k value;
	struct BSTNode<k>* left;
	struct BSTNode<k>* right;

	BSTNode(const k& val)
		:value(val), left(nullptr), right(nullptr)
	{}
};

template<class k>
class BSTree
{
	typedef BSTNode<k> Node;
public:
	bool Insert(const k& val)
	{
		//根节点为空格,直接插入
		if (root == nullptr)
		{
			root = new Node(val);
			return true;
		}

		//根节点不为空,先找到合适的位置,在插入
		Node* parent = root;
		Node* cur = root;
		while (cur)
		{
			//如果找到相同节点,则不需要插入
			if (cur->value == val)
			{
				return false;
			}
			else if (cur->value < val)
			{
				parent = cur;
				cur = cur->right;
			}
			else
			{
				parent = cur;
				cur = cur->left;
			}
		}
		//找到了,创建节点进行插入
		Node* tmp = new Node(val);
		if (parent->value < val)
		{
			parent->right = tmp;
		}
		else
		{
			parent->left = tmp;
		}

		return true;
	}

	const Node* find(const k& val)
	{
		Node* cur = root;
		while (cur)
		{
			if (cur->value == val)
			{
				return cur;
			}
			else if (cur->value < val)
			{
				cur = cur->right;
			}
			else
			{
				cur = cur->left;
			}
		}
		return nullptr;
	}

	bool Erase(const k& val)
	{
		//查找
		Node* parent = nullptr;
		Node* cur = root;
		while (cur)
		{
			if (cur->value > val)
			{
				//在左子树
				parent = cur;
				cur = cur->left;
			}
			else if (cur->value < val)
			{
				//在右子树找
				parent = cur;
				cur = cur->right;
			}
			else
			{
				//找到了,删除
				//左子树为空
				if (cur->left == nullptr)
				{
					if (cur == root)
						root = cur->right;
					//父节点指向右子树
					else
					{
						if (cur == parent->left)
						{
							parent->left = cur->right;
						}
						else
						{
							parent->right = cur->right;
						}
					}
				}
				else if (cur->right == nullptr)
				{
					if (cur == root)
						root = cur->left;
					else
					{
						if (cur == parent->left)
							parent->left = cur->left;
						else
							parent->right = cur->left;
					}
				}
				else
				{
					//左右子树都不为空,找右树的最小节点
					Node* minNode = cur->right;
					Node* minNodeParent = cur;
					while (minNode->left)
					{
						minNodeParent = minNode;
						minNode = minNode->left;
					}

					//将要删除的节点和该节点交换
					cur->value = minNode->value;
					//删除交换后的节点
					if (minNodeParent->left == minNode)
						minNodeParent->left = minNode->right;
					else
						minNodeParent->right = minNode->right;

					//删除节点
					delete minNode;
				}
				return true;
			}
		}
		return false;
	}
public:
	Node* root = nullptr;
};

#include "BSTree.h"

void print(BSTNode<int>* node)
{
	if (node == nullptr)
		return;
	std::cout << node->value<<" ";

	print(node->left);
	print(node->right);
}

void test()
{
	BSTree<int> bt;
	bt.Insert(5);
	bt.Insert(2);
	bt.Insert(6);
	bt.Insert(8);
	bt.Insert(1);
	bt.Insert(6);
	bt.Insert(8);
	bt.Insert(8);

	//中序打印二叉树
	print(bt.root);

	if (bt.find(2) != nullptr)
		std::cout << "找到了" << std::endl;
	bt.Erase(2);
	print(bt.root);


}

int main()
{
	test();
	return 0;
}

四、二叉搜索树的两种模型

1、K模型

K模型是指,在二叉搜索树的节点中,只有key值作为关键码。关键码即为搜索到的价值。

  • 例如,将一组数据以二叉搜索树的结构进行存储,需要找某一个数据时直接在二叉搜索树中查找该值是否存在,如果存在则输出。
  • 在这个示例中,二叉搜索树的每一个节点中都只存储了一个数作为key值。

2、KV模型

KV模型是指,每一个关键码key,都有与之对应的值Value,即<Key, Value>的键值对。

  • 例如,英汉语词典中,给定一个英语单词要求输出汉语意思。这里,英语单词就是key,该英语单词对应的汉语就是Value.
  • 在这个二叉搜树中,每个节点需要存两个关键码key和value,即英语单词和对应的中文。
  • 在输入一个英语单词时,只需要根据输入的英语单词在二叉搜索树中进行查找,并输出相应的中文意思。
  • 需要注意的是,在KV模型中,只需要根据key值在二叉树中进行查找。

实现一个KV模型的英译汉词典

#pragma once
#include<iostream>
#include<string>
#include<stack>

template<class K,class V>
struct Node
{
	K key;
	V value;
	Node<K, V>* left;
	Node<K, V>* right;

	//构造函数,通过指定的key和value值创建节点
	Node(const K& _key, const V& _val)
		:key(_key), value(_val)
		, left(nullptr), right(nullptr)
	{}
};

template<class K,class V>
class BSTreeKV
{
	typedef Node<K, V> Node;
public:
	//默认构造一个空的,只有当插入节点时才创建root节点
	//插入一个节点
	void insert(const K& key, const V& value)
	{
		Node* newNode = new Node(key, value);
		//如果根节点为空,直接插入
		if (root == nullptr)
			root = newNode;
		else
		{
			//找到一个合适的插入位置,进行插入
			Node* cur = root;
			while (cur->left || cur->right)
			{
				if (cur->key > newNode->key)
				{
					if (cur->left == nullptr)
						break;
					cur = cur->left;
				}
				else if (cur->key < newNode->key)
				{
					if (cur->right == nullptr)
						break;
					cur = cur->right;
				}
				else 
				{
					//已经存在该节点, 不重复插入
					return;
				}
			}
			//判断prev和cur的关系,将newNode插入
			if (cur->key > newNode->key)
			{
				cur->left = newNode;
			}
			else
			{
				cur->right = newNode;
			}
		}
	}

	//查找
	void find(const K& k)
	{
		Node* cur = root;
		while (cur)
		{
			if (cur->key > k)
			{
				cur = cur->left;
			}
			else if (cur->key < k)
			{
				cur = cur->right;
			}
			else
			{
				std::cout << k << "->"<<cur->value<<std::endl;
				break;
			}
		}
		if (cur == nullptr)
		{
			std::cout << "没有找到该节点" << std::endl;
		}
	}

	//删除
	void erase(const K& k)
	{
		Node* parent = nullptr;
		Node* cur = root;
		while (cur)
		{
			if (cur->key > k)
			{
				parent = cur;
				cur = cur->left;
			}
			else if (cur->key < k)
			{
				parent = cur;
				cur = cur->right;
			}
			else
			{
				//找到了要删除的节点
				//左子树为或者右子树为空,根节点指向cur的;另一个孩子节点
				if (cur->left == nullptr)
				{
					if (parent->left == cur)
					{
						parent->left = cur->right;
					}
					else
						parent->right = cur->right;
				}
				else if (cur->right == nullptr)
				{
					if (parent->left == cur)
					{
						parent->left = cur->left;
					}
					else
						parent->right = cur->left;
				}
				//左右子树都不为空,找最左节点进行替换
				else
				{
					Node* tmp = cur;
					while (tmp->left)
					{
						parent = tmp;
						tmp = tmp->left;
					}
					//交换cur和tmp
					cur->key = tmp->key;
					cur->value = tmp->value;
					if (tmp->right)
					{
						parent->left = tmp->right;
					}
					else
						parent->left = nullptr;
					delete tmp;
				}
				//删除成功
				std::cout << "删除成功" << std::endl;
				break;
			}
		}
		if (cur == nullptr)
		{
			std::cout << "没有找到要删除的节点"<<std::endl;
		}
	}

	//非递归---前序遍历
	void prevPrint()
	{
		std::stack<Node*> st;
		Node* cur = root;
		while (cur || !st.empty())
		{
			//打印并将左路节点入栈
			while (cur)
			{
				st.push(cur);
				std::cout << "<" << cur->key << "," << cur->value << ">" << " ";
				cur = cur->left;
			}
			//左路节点出栈并将右孩子节点入栈
			Node* tmp = st.top();
			st.pop();
			cur = tmp->right;
		}
		std::cout << std::endl;
	}
private:
	Node* root = nullptr;
};


五、二叉搜索树性能分析

二叉搜索树中,无论是插入一个节点还是删除一个节点都首先要进行查找。二叉树的查找时间复杂度为树的深度的函数。

最优情况:该二叉搜索树是一个完全二叉树,深度为logN,时间复杂度为O(logN)

最差情况:该二叉树时一个单支树,深度为N,时间复杂度为O(N)

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

疯狂嘚程序猿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值