C++ --模拟实现搜索二叉树

news2024/12/26 11:50:05

文章目录

  • #搜索二叉树
  • 1. 搜索二叉树特点
  • 2. 操作分析
    • 2.0 结点结构
    • 2.1 插入
    • 2.2 升序查看
    • 2.3 查找
    • 2.4 删除
    • 2.5 前序拷贝构造
  • 3. 完整代码
  • 4. 时间复杂度分析
  • 5. 简单应用
    • 5.1 字典搜索
    • 5.2 统计次数

#搜索二叉树

1. 搜索二叉树特点

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

见一见:

2. 操作分析

2.0 结点结构

template <class KEY>
struct node
{
	node<KEY>* _left;
	node<KEY>* _right;
	KEY _key;

	node(const KEY& key)
		:_left(nullptr)
		, _right(nullptr)
		, _key(key)
	{}
};
//声明:
//typedef struct node<KEY> node;
//typedef node* node_pointer;

2.1 插入

  • 非递归插入
bool insert_nonRecurse(const KEY& key)
{
	//a. 树为空
	if (_root == nullptr)
	{
		_root = new node(key);
		return true;
	}

	//b. 树不为空,按性质插入
	node_pointer cur = _root;
	node_pointer parent = nullptr;
	//b.1 找合适位置
	while (cur != nullptr)
	{
		if (key > cur->_key) //b.1.1 key > cur->_key --> 右遍历
		{
			parent = cur;
			cur = cur->_right;
		}
		else if (key < cur->_key) //b.1.2 key < cur->_key --> 左遍历
		{
			parent = cur;
			cur = cur->_left;
		}
		else //b.1.3 相等 --> false
		{
			return false;
		}
	}

	//b.2 连接
	cur = new node(key);
	if (key > cur->_key)
	{
		parent->right = cur;
	}
	else
	{
		parent->_left = cur;
	}

	return true;
}
  • 递归插入
bool insert_recurse(node_pointer& root, const KEY& key)
{
	//a. 树为空
	if (root == nullptr)
	{
		root = new node(key);
		return true;
	}

	//b. 树不为空
	if (key > root->_key)  //右遍历
	{
		return insert_recurse(root->_right, key);
	}
	else if (key < root->_key) //左遍历
	{
		return insert_recurse(root->_left, key);
	}
	else //相等 --> false
	{
		return false;
	}
}
  • node_pointer& 理解

2.2 升序查看

void inOrder(node_pointer root)
{
	if (root == nullptr)
		return;
	_inOrder(root->_left);
	cout << root->_key << " ";
	_inOrder(root->_right);
}

2.3 查找

bool find(const KEY& key)
{
	node_pointer cur = _root;
	while (cur != nullptr)
	{
		if (key > cur->_key) //右遍历
		{
			cur = cur->_right;
		}
		else if (key < cur->_key) //左遍历
		{
			cur = cur->_left;
		}
		else
		{
			return true;
		}
	}
	return false;
}

2.4 删除

  • 非递归删除
  1. 删除左右子树都为空的节点(叶子节点)
    • 找到节点
    • 判断是否为头节点(直接删除,头节点置空)
    • 删除节点
  1. 删除左子树为空,右子树不为空
    • 找到节点
    • 判断被删除节点是否为头节点(单独处理,头节点指向头节点右子树)
    • 判断被删除节点在其父节点左子树还是右子树中(连接问题要分支考虑)
      • 被删除节点在父节点左子树中(父节点左子树指向被删除节点右子树)
      • 被删除节点在父节点右子树中(父节点右子树指向被删除节点右子树)
    • 删除节点
  1. 删除左子树不为空,右子树为空
    • 找到节点
    • 判断被删除节点是否为头节点(单独处理,头节点指向头节点左子树)
    • 判断被删除节点在其父节点左子树还是右子树中(连接问题要分支考虑)
      • 被删除节点在父节点左子树中(父节点左子树指向被删除节点左子树)
      • 被删除节点在父节点右子树中(父节点右子树指向被删除节点左子树)
    • 删除节点
  1. 删除左右子树都不为空(右子树最小值交换策略)
    • 找到节点
    • 取被删除节点右子树最小值节点并得到其最小值节点的父节点
    • 被删除节点右子树最小值节点和被删除节点值交换(被删除节点变为原来右子树最小值节点位置)
    • 其改变后的被删除节点在其父节点左子树还是右子树中(连接问题要分支考虑)
      • 改变后的被删除节点在父节点左子树中(父节点左子树指向被删除节点右子树)
      • 改变后的被删除节点在父节点右子树中(父节点右子树指向被删除节点右子树)
    • 删除改变后的被删除节点
bool erase_nonRecurse(const KEY& key)
{
	node_pointer parent = nullptr;
	node_pointer cur = _root;

	while (cur != nullptr)
	{
		if (key > cur->_key) //右遍历
		{
			parent = cur;
			cur = cur->_right;
		}
		else if (key < cur->_key) //左遍历
		{
			parent = cur;
			cur = cur->_left;
		}
		else //相等 --> 删除
		{
			/*被删除节点要区分是在其父节点的左子树还是右子树中*/
			if (cur->_left == nullptr) //被删节点左子树为空 && 被删节点左右子树都为空
			{
				if (cur == _root) //头节点处理
				{
					_root = cur->_right;
				}
				else
				{
					if (parent->_left == cur) //被删除节点在父节点左子树
					{
						parent->_left = cur->_right;
					}
					else //被删除节点在父节点右子树
					{
						parent->_right = cur->_right;
					}
				}

				delete cur;
			}
			else if (cur->_right == nullptr) //被删节点右子树为空 && 被删节点左右子树都为空
			{
				if (cur == _root) //头节点处理
				{
					_root = cur->_left;
				}
				else
				{
					if (parent->_left == cur) //被删除节点在父节点左子树
					{
						parent->_left = cur->_left;
					}
					else //被删除节点在父节点右子树
					{
						parent->_right = cur->_left;
					}
				}

				delete cur;
			}
			else //左右子树都不为空 (策略:右子树最小值交换)
			{
				node_pointer min_right = cur->_right;
				node_pointer parent_min_right = cur;
				while (min_right->_left) //取右子树最小值节点
				{
					parent_min_right = min_right;
					min_right = min_right->_left;
				}

				swap(min_right->_key, cur->_key);
				//被删除节点父节点和其节点右子树连接(支持原因:右子树最小值只可能拥有右子树不可能拥有左子树)
				if (parent_min_right->_left == min_right)  //右子树最小值节点(被删除节点)在其父节点左子树
				{
					parent_min_right->_left = min_right->_right;
				}
				else  //右子树最小值节点在其父节点右子树
				{
					parent_min_right->_right = min_right->_right;
				}

				delete min_right;
			}
			return true;
		}
	}
	return false;
}
  • 递归删除

bool _erase_Recurse(node_pointer& root, const KEY& key)
{
	if (root == nullptr)
	{
		return false;
	}

	if (key > root->_key) //右遍历
	{
		_erase_Recurse(root->_right, key);
	}
	else if (key < root->_key) //左遍历
	{
		_erase_Recurse(root->_left, key);
	}
	else //相等 --> 删除
	{
		node_pointer deleted_node = root;

		if (root->_left == nullptr) //被删节点左子树为空 && 被删节点左右子树都为空
		{
			root = root->_right;
		}
		else if (root->_right == nullptr) //被删节点右子树为空 && 被删节点左右子树都为空
		{
			root = root->_left;
		}
		else //左右子树都不为空(策略:右子树最小值和被删节点交换)
		{
			node_pointer min_right = root->_right;
			while (min_right->_left) //取被删节点右子树最小值
			{
				min_right = min_right->_left;
			}

			//交换数值
			swap(min_right->_key, root->_key);

			return _erase_Recurse(root->_right, key); //交换后删除值为min_right节点(右子树遍历)
		}

		delete deleted_node;

		return true;
	}
}
  • 删除小结

首先考虑被删节点的左右子树都不为空&&被删节点的左子树为空右子树不为空&&被删节点的右子树为空左子树不为空&&被删节点左右子树都不为空四种情况(其中,被删节点的左子树为空右子树不为空&&被删节点的右子树为空左子树不为空包含被删节点的左右子树都不为空的情况)

2.5 前序拷贝构造

binary_search_tree(const binary_search_tree<KEY>& tree)
{
	_root = _copy(tree._root);
}

node_pointer _copy(node_pointer root)
{
	if (root == nullptr)
	{
		return nullptr;
	}

	node_pointer new_node = new node(root->_key);
	new_node->_left = _copy(root->_left);
	new_node->_right = _copy(root->_right);
	return new_node;
}

3. 完整代码

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

//模拟实现二叉搜索树

namespace my_tree
{
	template <class KEY>
	struct node
	{
		node<KEY>* _left;
		node<KEY>* _right;
		KEY _key;

		node(const KEY& key)
			:_left(nullptr)
			,_right(nullptr)
			,_key(key)
		{}
	};

	template <class KEY>
	class binary_search_tree
	{
	private:
		typedef	node<KEY> node;
		typedef node* node_pointer;

	public:
		binary_search_tree()
			:_root(nullptr)
		{}

		binary_search_tree(const binary_search_tree<KEY>& tree)
		{
			_root = _copy(tree._root);
		}

		binary_search_tree<KEY>& operator=(binary_search_tree<KEY>& tree)
		{
			_swap(_root, tree._root);
			return *this;
		}

		~binary_search_tree()
		{
			_destory(_root);
		}

		void _destory(node_pointer root)
		{
			if (root == nullptr)
			{
				return;
			}

			_destory(root->_left);
			_destory(root->_right);
			delete root;
			root = nullptr;
		}

		bool insert_nonRecurse(const KEY& key)
		{
			//a. 树为空
			if (_root == nullptr)
			{
				_root = new node(key);
				return true;
			}

			//b. 树不为空,按性质插入
			node_pointer cur = _root;
			node_pointer parent = nullptr;
			//b.1 找合适位置
			while (cur != nullptr) 
			{
				if (key > cur->_key) //b.1.1 key > cur->_key --> 右遍历
				{
					parent = cur;
					cur = cur->_right;
				}
				else if (key < cur->_key) //b.1.2 key < cur->_key --> 左遍历
				{
					parent = cur;
					cur = cur->_left;
				}
				else //b.1.3 相等 --> false
				{
					return false;
				}
			}

			//b.2 连接
			cur = new node(key);
			if (key > parent->_key) //b.2.1 key > parent_key --> 右连接
			{
				parent->_right = cur;
			}
			else //b.2.2 key < parent_key --> 左连接
			{
				parent->_left = cur;
			}

			return true;
		}

		bool insert_recurse(const KEY& key)
		{
			return _insert_recurse(_root, key);
		}

		bool find(const KEY& key)
		{
			node_pointer cur = _root;
			while (cur != nullptr)
			{
				if (key > cur->_key)
				{
					cur = cur->_right;
				}
				else if (key < cur->_key)
				{
					cur = cur->_left;
				}
				else
				{
					return true;
				}
			}
			return false;
		}

		bool erase_nonRecurse(const KEY& key)
		{
			node_pointer parent = nullptr;
			node_pointer cur = _root;
			
			while (cur != nullptr)
			{ 
				if (key > cur->_key) //右遍历
				{
					parent = cur;
					cur = cur->_right;
				}
				else if (key < cur->_key) //左遍历
				{
					parent = cur;
					cur = cur->_left;
				}
				else //相等 --> 删除
				{
					/*被删除节点要区分是在其父节点的左子树还是右子树中*/
					if (cur->_left == nullptr) //左子树为空 && 左右子树都为空
					{
						if (cur == _root) //头节点处理
						{
							_root = cur->_right;
						}
						else
						{
							if (parent->_left == cur) //被删除节点在父节点左子树
							{
								parent->_left = cur->_right;
							}
							else //被删除节点在父节点右子树
							{
								parent->_right = cur->_right;
							}
						}

						delete cur;
					}
					else if (cur->_right == nullptr) //右子树为空 && 左右子树都为空
					{ 
						if (cur == _root) //头节点处理
						{
							_root = cur->_left;
						}
						else
						{
							if (parent->_left == cur) //被删除节点在父节点左子树
							{
								parent->_left = cur->_left;
							}
							else //被删除节点在父节点右子树
							{
								parent->_right = cur->_left;
							}
						}

						delete cur;
					}
					else //左右子树都不为空 (策略:右子树最小值交换)
					{
						node_pointer min_right = cur->_right; 
						node_pointer parent_min_right = cur;
						while (min_right->_left) //取右子树最小值节点
						{
							parent_min_right = min_right;
							min_right = min_right->_left;
						}
						
						swap(min_right->_key, cur->_key); 
						//被删除节点父节点和其节点右子树连接(支持原因:右子树最小值只可能拥有右子树不可能拥有左子树)
						if (parent_min_right->_left == min_right)  //右子树最小值节点(被删除节点)在其父节点左子树
						{
							parent_min_right->_left = min_right->_right;
						}
						else  //右子树最小值节点在其父节点右子树
						{
							parent_min_right->_right = min_right->_right;
						}
						
						delete min_right;
					}
					return true;
				}
			}
			return false;
		}

		bool erase_Recurse(const KEY& key)
		{
			return _erase_Recurse(_root, key);
		}


		
		void inOrder()
		{
			_inOrder(_root);
			cout << endl;
		}


	private:
		void _inOrder(node_pointer root)
		{
			if (root == nullptr)
				return;
			_inOrder(root->_left);
			cout << root->_key << " ";
			_inOrder(root->_right);
		}

		bool _insert_recurse(node_pointer& root, const KEY& key)
		{
			//a. 树为空
			if (root == nullptr)
			{
				root = new node(key);
				return true;
			}

			//b. 树不为空
			if (key > root->_key)  //右遍历
			{
				return _insert_recurse(root->_right, key);
			}
			else if (key < root->_key) //左遍历
			{
				return _insert_recurse(root->_left, key);
			}
			else //相等 --> false
			{
				return false;
			}
		}

		bool _erase_Recurse(node_pointer& root, const KEY& key)
		{
			if (root == nullptr)
			{
				return false;
			}

			if (key > root->_key) //右遍历
			{
				_erase_Recurse(root->_right, key);
			}
			else if (key < root->_key) //左遍历
			{
				_erase_Recurse(root->_left, key);
			}
			else //相等 --> 删除
			{
				node_pointer deleted_node = root;

				if (root->_left == nullptr) //被删节点左子树为空 && 被删节点左右子树都为空
				{
					root = root->_right;
				}
				else if (root->_right == nullptr) //被删节点右子树为空 && 被删节点左右子树都为空
				{
					root = root->_left;
				}
				else //左右子树都不为空
				{
					node_pointer min_right = root->_right;
					while (min_right->_left) //取被删节点右子树最小值
					{
						min_right = min_right->_left;
					}

					//交换数值
					swap(min_right->_key, root->_key);

					return _erase_Recurse(root->_right, key);
				}
				
				delete deleted_node;

				return true;
			}
		}

		node_pointer _copy(node_pointer root)
		{
			if (root == nullptr)
			{
				return nullptr;
			}

			node_pointer new_node = new node(root->_key);
			new_node->_left = _copy(root->_left);
			new_node->_right = _copy(root->_right);
			return new_node;
		}

		void _swap(node_pointer& self, node_pointer& other)
		{
			node_pointer tmp = self;
			self = other;
			other = tmp;
		}

		private:
			node_pointer _root;
	};
}

4. 时间复杂度分析

增删查改时间复杂度:O(N)

5. 简单应用

5.1 字典搜索

namespace key_value
{
	template <class KEY, class VALUE>
	struct node
	{
		struct node<KEY, VALUE>* _left;
		struct node<KEY, VALUE>* _right;
		KEY _key;
		VALUE _value;

		node(const KEY& key, const VALUE& value)
			:_left(nullptr)
			, _right(nullptr)
			, _key(key)
			, _value(value)
		{}
	};

	template <class KEY, class VALUE>
	class binary_search_tree
	{
	private:
		typedef struct node<KEY, VALUE> node;
		typedef node* node_pointer;

	public:
		binary_search_tree()
			:_root(nullptr)
		{}

		~binary_search_tree()
		{
			_destory(_root);
		}

		void _destory(node_pointer root)
		{
			if (root == nullptr)
			{
				return;
			}

			_destory(root->_left);
			_destory(root->_right);
			delete root;
			root = nullptr;
		}

		bool insert_nonRecurse(const KEY& key, const VALUE& value)
		{
			//a. 树为空
			if (_root == nullptr)
			{
				_root = new node(key, value);
				return true;
			}

			//b. 树不为空,按性质插入
			node_pointer cur = _root;
			node_pointer parent = nullptr;
			//b.1 找合适位置
			while (cur != nullptr)
			{
				if (key > cur->_key) //b.1.1 key > cur->_key --> 右遍历
				{
					parent = cur;
					cur = cur->_right;
				}
				else if (key < cur->_key) //b.1.2 key < cur->_key --> 左遍历
				{
					parent = cur;
					cur = cur->_left;
				}
				else //b.1.3 相等 --> false
				{
					return false;
				}
			}

			//b.2 连接
			cur = new node(key, value);
			if (key > parent->_key) //b.2.1 key > parent_key --> 右连接
			{
				parent->_right = cur;
			}
			else //b.2.2 key < parent_key --> 左连接
			{
				parent->_left = cur;
			}

			return true;
		}

		node_pointer find(const KEY& key)
		{
			node_pointer cur = _root;
			while (cur != nullptr)
			{
				if (key > cur->_key)
				{
					cur = cur->_right;
				}
				else if (key < cur->_key)
				{
					cur = cur->_left;
				}
				else
				{
					return cur;
				}
			}
			return nullptr;
		}

		bool erase_nonRecurse(const KEY& key)
		{
			node_pointer parent = nullptr;
			node_pointer cur = _root;

			while (cur != nullptr)
			{
				if (key > cur->_key) //右遍历
				{
					parent = cur;
					cur = cur->_right;
				}
				else if (key < cur->_key) //左遍历
				{
					parent = cur;
					cur = cur->_left;
				}
				else //相等 --> 删除
				{
					/*被删除节点要区分是在其父节点的左子树还是右子树中*/
					if (cur->_left == nullptr) //左子树为空 && 左右子树都为空
					{
						if (cur == _root) //头节点处理
						{
							_root = cur->_right;
						}
						else
						{
							if (parent->_left == cur) //被删除节点在父节点左子树
							{
								parent->_left = cur->_right;
							}
							else //被删除节点在父节点右子树
							{
								parent->_right = cur->_right;
							}
						}

						delete cur;
					}
					else if (cur->_right == nullptr) //右子树为空 && 左右子树都为空
					{
						if (cur == _root) //头节点处理
						{
							_root = cur->_left;
						}
						else
						{
							if (parent->_left == cur) //被删除节点在父节点左子树
							{
								parent->_left = cur->_left;
							}
							else //被删除节点在父节点右子树
							{
								parent->_right = cur->_left;
							}
						}

						delete cur;
					}
					else //左右子树都不为空 (策略:右子树最小值交换)
					{
						node_pointer min_right = cur->_right;
						node_pointer parent_min_right = cur;
						while (min_right->_left) //取右子树最小值节点
						{
							parent_min_right = min_right;
							min_right = min_right->_left;
						}

						swap(min_right->_key, cur->_key);
						//被删除节点父节点和其节点右子树连接(支持原因:右子树最小值只可能拥有右子树不可能拥有左子树)
						if (parent_min_right->_left == min_right)  //右子树最小值节点(被删除节点)在其父节点左子树
						{
							parent_min_right->_left = min_right->_right;
						}
						else  //右子树最小值节点在其父节点右子树
						{
							parent_min_right->_right = min_right->_right;
						}

						delete min_right;
					}
					return true;
				}
			}
			return false;
		}

		void inOrder()
		{
			_inOrder(_root);
			cout << endl;
		}

	private:
		void _inOrder(node_pointer root)
		{
			if (root == nullptr)
				return;
			_inOrder(root->_left);
			cout << root->_key << " " << root->_value << endl;
			_inOrder(root->_right);
		}

	private:
		node_pointer _root;
	};
	
	void test()
	{
		binary_search_tree<string, string> dict;
		dict.insert_nonRecurse("apple", "苹果");
		dict.insert_nonRecurse("banana", "香蕉");
		dict.insert_nonRecurse("pear", "梨子");
		dict.insert_nonRecurse("orange", "橙子");
		dict.insert_nonRecurse("strawberry", "草莓");
		dict.insert_nonRecurse("pitaya", "火龙果");
		dict.insert_nonRecurse("tangerine", "橘子");

		string str;
		while (cin >> str)
		{
			node<string, string>* ret = dict.find(str);
			if (ret != nullptr)
				cout << ret->_key << "->" << ret->_value << endl;
			else
				cout << "找不到" << endl;
		}

	}

}

5.2 统计次数

namespace key_value
{
	template <class KEY, class VALUE>
	struct node
	{
		struct node<KEY, VALUE>* _left;
		struct node<KEY, VALUE>* _right;
		KEY _key;
		VALUE _value;

		node(const KEY& key, const VALUE& value)
			:_left(nullptr)
			, _right(nullptr)
			, _key(key)
			, _value(value)
		{}
	};

	template <class KEY, class VALUE>
	class binary_search_tree
	{
	private:
		typedef struct node<KEY, VALUE> node;
		typedef node* node_pointer;

	public:
		binary_search_tree()
			:_root(nullptr)
		{}

		~binary_search_tree()
		{
			_destory(_root);
		}

		void _destory(node_pointer root)
		{
			if (root == nullptr)
			{
				return;
			}

			_destory(root->_left);
			_destory(root->_right);
			delete root;
			root = nullptr;
		}

		bool insert_nonRecurse(const KEY& key, const VALUE& value)
		{
			//a. 树为空
			if (_root == nullptr)
			{
				_root = new node(key, value);
				return true;
			}

			//b. 树不为空,按性质插入
			node_pointer cur = _root;
			node_pointer parent = nullptr;
			//b.1 找合适位置
			while (cur != nullptr)
			{
				if (key > cur->_key) //b.1.1 key > cur->_key --> 右遍历
				{
					parent = cur;
					cur = cur->_right;
				}
				else if (key < cur->_key) //b.1.2 key < cur->_key --> 左遍历
				{
					parent = cur;
					cur = cur->_left;
				}
				else //b.1.3 相等 --> false
				{
					return false;
				}
			}

			//b.2 连接
			cur = new node(key, value);
			if (key > parent->_key) //b.2.1 key > parent_key --> 右连接
			{
				parent->_right = cur;
			}
			else //b.2.2 key < parent_key --> 左连接
			{
				parent->_left = cur;
			}

			return true;
		}

		node_pointer find(const KEY& key)
		{
			node_pointer cur = _root;
			while (cur != nullptr)
			{
				if (key > cur->_key)
				{
					cur = cur->_right;
				}
				else if (key < cur->_key)
				{
					cur = cur->_left;
				}
				else
				{
					return cur;
				}
			}
			return nullptr;
		}

		bool erase_nonRecurse(const KEY& key)
		{
			node_pointer parent = nullptr;
			node_pointer cur = _root;

			while (cur != nullptr)
			{
				if (key > cur->_key) //右遍历
				{
					parent = cur;
					cur = cur->_right;
				}
				else if (key < cur->_key) //左遍历
				{
					parent = cur;
					cur = cur->_left;
				}
				else //相等 --> 删除
				{
					/*被删除节点要区分是在其父节点的左子树还是右子树中*/
					if (cur->_left == nullptr) //左子树为空 && 左右子树都为空
					{
						if (cur == _root) //头节点处理
						{
							_root = cur->_right;
						}
						else
						{
							if (parent->_left == cur) //被删除节点在父节点左子树
							{
								parent->_left = cur->_right;
							}
							else //被删除节点在父节点右子树
							{
								parent->_right = cur->_right;
							}
						}

						delete cur;
					}
					else if (cur->_right == nullptr) //右子树为空 && 左右子树都为空
					{
						if (cur == _root) //头节点处理
						{
							_root = cur->_left;
						}
						else
						{
							if (parent->_left == cur) //被删除节点在父节点左子树
							{
								parent->_left = cur->_left;
							}
							else //被删除节点在父节点右子树
							{
								parent->_right = cur->_left;
							}
						}

						delete cur;
					}
					else //左右子树都不为空 (策略:右子树最小值交换)
					{
						node_pointer min_right = cur->_right;
						node_pointer parent_min_right = cur;
						while (min_right->_left) //取右子树最小值节点
						{
							parent_min_right = min_right;
							min_right = min_right->_left;
						}

						swap(min_right->_key, cur->_key);
						//被删除节点父节点和其节点右子树连接(支持原因:右子树最小值只可能拥有右子树不可能拥有左子树)
						if (parent_min_right->_left == min_right)  //右子树最小值节点(被删除节点)在其父节点左子树
						{
							parent_min_right->_left = min_right->_right;
						}
						else  //右子树最小值节点在其父节点右子树
						{
							parent_min_right->_right = min_right->_right;
						}

						delete min_right;
					}
					return true;
				}
			}
			return false;
		}

		void inOrder()
		{
			_inOrder(_root);
			cout << endl;
		}

	private:
		void _inOrder(node_pointer root)
		{
			if (root == nullptr)
				return;
			_inOrder(root->_left);
			cout << root->_key << " " << root->_value << endl;
			_inOrder(root->_right);
		}

	private:
		node_pointer _root;
	};

	void test()
	{
		string arr[] = { "西瓜", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉", "梨" }; 
		key_value::binary_search_tree<string, int> countTree;
		for (auto str : arr)
		{
			auto ret = countTree.find(str);
			if (ret == nullptr)
			{
				countTree.insert_nonRecurse(str, 1);
			}
			else
			{
				ret->_value++;
			}
		}

		countTree.inOrder();
	}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/425444.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Dockerfile及新型容器镜像构建技术

文章目录一、容器镜像分类1、操作系统类2、应用类二、容器镜像获取方法1、在dockerhub直接下载2、把操作系统中文件系统打包为容器镜像3、把正在运行的容器打包为容器镜像&#xff0c;即docker commit4、通过dockerfile实现容器镜像的自定义以及生成三、dockerfile1、dockerfil…

数据库管理系统PostgreSQL部署安装完整教程

PostgreSQL是一个开源的关系型数据库管理系统&#xff0c;它支持大量的数据类型和复杂的查询语言&#xff0c;可以用于各种应用程序。它是一个高性能的数据库&#xff0c;可以处理大量的数据&#xff0c;并且具有良好的可扩展性和可靠性。 目录 一.Linux系统安装PostgresSQL&a…

机器学习——线性模型之Softmax回归

问&#xff1a;Softmax回归模型是一种典型处理多分类任务的非线性分类模型 答&#xff1a;错误。Softmax回归是线性分类模型。实际上是逻辑回归的拓展&#xff0c;它将逻辑回归的二分类推广到了多分类&#xff0c;用逻辑回归的方法解决多分类问题。 线性模型——Softmax回归 …

Linux之基础IO

文章目录一.关于文件的共识二.复习C语言的文件操作1.打开文件2.向文件中写入数据3.向文件中追加数据三.有关文件的系统调用1.open&#xff08;文件打开&#xff09;2.write&#xff08;向文件写入&#xff09;3.read(读文件)四.文件描述符1.进程如何找到自己的文件2.为什么文件…

leetcode刷题(3)

各位朋友们大家好&#xff0c;今天是我leedcode刷题系列的第三篇&#xff0c;废话不多说&#xff0c;直接进入主题。 文章目录分割链表题目要求用例输入提示做题思路c语言代码实现Java代码实现相交链表题目要求用例输入提示做题思路c语言实现代码Java代码实现分割链表 leetcod…

KDSL-82轻型升流器

一、产品概述 KDSL-82 1000A大电流发生器是一种作为检验用的电流源&#xff0c;大电流试验器采用ARM芯片控制输出工艺和大容量的环形变压器&#xff0c;并且配有液晶屏显示的表计&#xff0c;同时显示一、二次电流、变比和秒表接点(或电位)的动作时间。外配铝合金机箱&#xff…

OceanMind海睿思受邀参加中小企业数字化转型发展论坛

近日&#xff0c;由江苏省企业信息化协会主办的中小企业数字化转型发展论坛于南京圆满结束。论坛重点邀请了南京市中小企业制造标杆、专精特新“小巨人”企业等相关信息化负责人和IT工作者共同探讨中小企业数字化转型的发展路径。 OceanMind海睿思作为南京地区大数据领域优秀代…

自然语言大模型介绍

1 简介 最近一直被大语言模型刷屏。本文是周末技术分享会的提纲&#xff0c;总结了一些自然语言模型相关的重要技术&#xff0c;以及各个主流公司的研究方向和进展&#xff0c;和大家共同学习。 2 Transformer 目前的大模型基本都是Transformer及其变种。本部分将介绍Transf…

FPGA基于XDMA实现PCIE X4的HDMI视频采集 提供工程源码和QT上位机程序和技术支持

目录1、前言2、我已有的PCIE方案3、PCIE理论4、总体设计思路和方案5、vivado工程详解6、驱动安装7、QT上位机软件8、上板调试验证9、福利&#xff1a;工程代码的获取1、前言 PCIE&#xff08;PCI Express&#xff09;采用了目前业内流行的点对点串行连接&#xff0c;比起 PCI …

自动化测试用什么框架?Pytest框架 vs Unittest框架,企业使用分析......

目录&#xff1a;导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09;前言 安装方式不同 unit…

JavaWeb开发 —— 分层解耦

目录 一、三层架构 二、分层解耦 三、IOC & DI 入门 四、IOC控制反转详解 五、DI依赖注入详解 一、三层架构 在 JavaWeb开发 —— 请求响应 最后案例中我们编写的程序代码都是写在 Controller 当中。 而在我们实际软件设计和开发中&#xff0c;会尽量让每一个接口、类…

c++学习之类与对象2

目录 1.explicit关键字 类的对象数组 动态对象的创建与初始化 1.动态创建的概述 2.c语言方式创建动态对象 c对象的动态申请 1.new创建动态对象 2.delete释放动态对象 动态对象数组 静态成员 静态成员变量 静态成员函数 1.explicit关键字 explicit关键字 修饰构造函数…

查询淘宝商品详情页面数据(商品详情数据,商品销量数据,商品sku数据,商品视频数据,商品优惠券数据)接口代码封装教程

业务场景&#xff1a;作为全球最大的 B2C 电子商务平台之一&#xff0c;淘宝天猫平台提供了丰富的商品资源&#xff0c;吸引了大量的全球买家和卖家。为了方便开发者接入淘宝天猫平台&#xff0c;淘宝天猫平台提供了丰富的 API 接口&#xff0c;其中历史价格接口是非常重要的一…

策略设计模式(Strategy Pattern)[论点:概念、组成角色、相关图示、示例代码、适用场景]

文章目录概念组成角色相关图示示例代码适用场景概念 策略模式&#xff08;Strategy Pattern&#xff09;是一种行为设计模式&#xff0c;它定义了一系列的算法&#xff0c;并将每一个算法封装起来&#xff0c;使它们可以相互替换。策略模式使得算法可以独立于使用它的客户端变化…

4.12~4.13学习总结

File 相对路径和绝对路径的区别&#xff1a; 相对路径不带盘符&#xff0c;绝对路径带盘符 小知识点&#xff1a;1KB1024字节&#xff0c;1MB1024KB,1GB1024MB; File对象就表示一个路径&#xff0c;可也是文件的路径&#xff0c;也可以是文件夹的路径 这个路径可以是存在的也可…

API-MS-WIN-CRT-RUNTIME-L1-1-0.DLL丢失怎么解决?

API-MS-WIN-CRT-RUNTIME-L1-1-0.DLL是Windows系统中的一个非常重要的动态链接库文件&#xff0c;该文件的全称为“Application Programming Interface Microsoft Windows C Runtime Link Library”。这个DLL文件中包含多个函数库&#xff0c;可将这些函数库链接到应用程序中。在…

AC7811-FOC无感控制代码详解

目录 矢量控制原理 矢量控制框图 电流采样方式 电流在整个控制过程中的传递 采样关键点 三电阻 双电阻 单电阻 三者对比 坐标变换 dq轴电流的PI控制 启动方式 启动波形 脉冲注入 高频注入 Startup 预定位到指定角度 PulseInject_api hfi_api Speed loop s…

已解决:ModuleNotFoundError: No module named ‘flask._compat‘

📋 个人简介 💖 作者简介:大家好,我是阿牛,全栈领域新星创作者。😜🎉 支持我:点赞👍+收藏⭐️+留言📝📣 系列专栏:flask框架从入门到实战🍁💬格言:要成为光,因为有怕黑的人!🔥 相信很多人在flask项目中都遇到了这个报错:ModuleNotFoundError: No …

MyBatis动态SQL的使用

为什么需要使用动态sql? 在实际项目的开发中&#xff0c;开发人员在使用JDBC或其他持久层框架进行开发时&#xff0c;经常需要根据不同的条件拼接SQL语句&#xff0c;拼接SQL语句时还要确保不能遗漏必要的空格、标点符号等&#xff0c;这种编程方式给开发人员带来了非常大的不…

初探JdbcTemplate操作

文章目录一、创建数据库二、创建用户三、打开Spring项目四、添加数据库相关依赖五、创建用户实体类六、创建用户数据访问接口七、创建用户数据访问接口实现类八、创建用户服务类九、创建数据库配置属性文件十、创建Spring配置文件十一、创建用户服务测试类1、测试按编号查询用户…