• 二叉树进阶——手撕二叉搜索树


    troop主页:troop

    1.二叉搜索树的定义

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

    • 若它的左子树不为空,则左子树上的所有节点的值都小于根节点的值
    • 若它的右子树不为空,则右子树上的所有节点的值都大于根节点的值

    在这里插入图片描述

    2.实现(非递归)

    补充结构

    //struct BinarySearchTreeNode
    template<class K>
    struct BSTreeNode
    {
    	typedef BSTreeNode<K> Node;
    	Node* _left;
    	Node* _right;
    	K _key;
    
    	BSTreeNode(const K& key)
    		:_left(nullptr)
    		, _right(nullptr)
    		, _key(key)
    	{}
    };
    
    /class BinarySearchTree
    template<class K>
    class BSTree
    {
    	typedef BSTreeNode<K> Node;
    public:
    	BSTree() = default;
    	BSTree(const BSTree<K>& t)
    	{
    		_root = copy(t._root);
    	}
    	Node* copy(Node* root)
    	{
    		if (root == nullptr)
    			return nullptr;
    		Node* newroot = new Node(root->_val);
    		newroot->_left = copy(root->_left);
    		newroot->_right = copy(root->_right);
    		return newroot;
    	}
    
    	~BSTree()
    	{
    		Destroy();
    	}
    	void Destroy()
    	{
    		return _Destroy(_root);
    	}
    	void _Destroy(Node* root)
    	{
    		if (root == nullptr)
    			return;
    		_Destroy(root->_left);
    		_Destroy(root->_right);
    		delete root;
    	}
    private:
    	Node* _root;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56

    2.1查找

    根据它的定义查找就是,跟节点比,比节点大就去它的右子树中寻找,比他小就去它的左子树中寻找

    	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;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    2.2插入

    在这里插入图片描述
    插入也很简单,例如上图我们要插入16,我们就先找到要插入的位置,然后为了方便我们记录整个过程我们需要一个父节点。

    	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->_left=cur;
    		}
    		else
    		{
    			parent->_right=cur;
    		}
    		return true;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    现在可以进行测试代码的正确性。
    在这里插入图片描述
    注意,搜索二插入要有序它走的是中序遍历。

    2.3删除(重要

    删除比插入麻烦的多,我们删除值原则就是要保证它还是搜索二叉树,它的性质不可以改变。这就挺麻烦的,所以我们在删除这里用替换删除
    替换删除:找到一个可以替换的节点,交换值,转换删除它。
    可以替换的节点是:左子树的最大or右子树的最小。

    下面我们来分析一下删除的各种情况

    1. 删除孩子节点
    2. 删除只有一个孩子的节点
    3. 删除两个孩子的节点
      这里总结下,情况1和2可以归为一类。情况3我们就要用到替换删除法

    情况1(无孩子&&一个孩子)

    在这里插入图片描述

    //找到了开始删除
    				//第一种
    				if (cur->_left == nullptr)//我的左为空
    				{
    					if (cur == _root)
    					{
    						_root = cur->_right;
    					}
    					else
    					{
    						if (cur == parent->_left)//我是父亲的左
    						{
    							parent->_left = cur->_right;
    						}
    						else//我是父亲的左
    						{
    							parent->_right = cur->_right;
    						}
    					}
    					delete cur;
    					return true;
    				}
    				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;
    						}
    					}
    					delete cur;
    					return true;
    				}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    ###情况二(两个孩子)
    在这里插入图片描述
    找到右子树的最小,把它的值复制给cur,就转换成了删除叶子节点

    				else//第二种(俩孩子)替换删除法
    				{
    					Node* rightMinparent = cur;
    					Node* rightMin = cur->_right;
    					while (cur->_right)
    					{
    						rightMinparent = rightMin;
    						rightMin = rightMin->_left;
    					}
    					cur->_key = rightMin->_key;
    					if (rightMin == rightMinparent->_left)
    					{
    						rightMinparent->_left = rightMin->_right;
    					}
    					else
    					{
    						rightMinparent->_right = rightMin->_right;
    					}
    					delete rightMin;
    					return true;
    				}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    3.二叉搜索树的应用

    3.1K模型

    K模型即只有key作为关键码,结构中只需要存储Key即可,关键码即为需要搜索到
    的值。
    比如:给一个单词word,判断该单词是否拼写正确。这个就是普通的二叉搜索树

    3.2KV模型

    KV模型:每一个关键码key,都有与之对应的值Value,即的键值对。
    通过key值快速查找另外一个值在不在。我们用二叉搜索树这个用的是比较多的
    生活中的KV模型例如:商场车库,进去都可以进入(记录车牌(key)进入时间(value))
    出:付费出(通过车牌(key),查询进入的时间(value))。
    还有字典查询,高铁身份证进站等等。

    3.2.1KV模型的实现

    这个就是多加了一个对象,实现直接CV上面的代码

    namespace key_value
    {
    	template<class K, class V>
    	struct BSTreeNode
    	{
    		typedef BSTreeNode<K, V> Node;
    		Node* _left;
    		Node* _right;
    		K _key;
    		V _value;
    
    		BSTreeNode(const K& key, const V& value)
    			:_left(nullptr)
    			, _right(nullptr)
    			, _key(key)
    			, _value(value)
    		{}
    	};
    
    
    	template<class K, class V>
    	class BSTree
    	{
    		typedef BSTreeNode<K, V> Node;
    	public:
    		//插入
    		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 true;
    				}
    			}
    
    			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;
    		}
    		//中序遍历
    		void _InOrder(Node* root)
    		{
    			if (root == nullptr)
    				return;
    			_InOrder(root->_left);
    			std::cout << root->_val << " ";
    			_InOrder(root->_right);
    		}
    
    		void InOrder()
    		{
    			_InOrder(_root);
    			cout << endl;
    		}
    		//3.删除
    		bool Erase(const K& key)
    		{
    			Node* parent = nullptr;
    			Node* cur = _root;
    			while (cur)
    			{
    				if (cur->_val < key)
    				{
    					parent = cur;
    					cur = cur->_right;
    				}
    				else if (cur->_val > 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;
    						return true;
    					}
    					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;
    						return true;
    					}
    
    					else//第二种(有两个孩子)替换删除法
    					{
    						Node* rightMinparent = cur;
    						Node* rightMin = cur->_right;
    						while (rightMin->_left)
    						{
    							rightMinparent = rightMin;
    							rightMin = rightMin->_left;
    						}
    						cur->_val = rightMin->_val;
    						if (rightMin == rightMinparent->_left)
    							rightMinparent->_left = rightMin->_right;
    						else
    							rightMinparent->_right = rightMin->_right;
    						delete rightMin;
    						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;
    	};
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203

    我们可以用哥=个例子来玩一玩这个KV模型

    void TestBSTree()
    {
    	key_value::BSTree<string, string> dict;
    	dict.Insert("insert", "插入");
    	dict.Insert("erase", "删除");
    	dict.Insert("left", "左边");
    	dict.Insert("string", "字符串");
    
    	string str;
    	while (cin >> str)
    	{
    		auto ret = dict.Find(str);
    		if (ret)
    		{
    			cout << str << ":" << ret->_value << endl;
    		}
    		else
    		{
    			cout << "单词拼写错误" << endl;
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    在这里插入图片描述

    总结

    总的来说二叉搜索树,比较难的地方就是删除部分,多画图多思考。
    下篇我们就要深入二叉搜索树。

    二叉搜索树源代码

    #pragma once
    #include
    using namespace std;
    //struct BinarySearchTreeNode
    template<class K>
    struct BSTreeNode
    {
    	typedef BSTreeNode<K> Node;
    	Node* _left;
    	Node* _right;
    	K _key;
    
    	BSTreeNode(const K& key)
    		:_left(nullptr)
    		, _right(nullptr)
    		, _key(key)
    	{}
    };
    
    //class BinarySearchTree
    template<class K>
    class BSTree
    {
    	typedef BSTreeNode<K> Node;
    public:
    	BSTree() = default;
    	BSTree(const BSTree<K>& t)
    	{
    		_root = copy(t._root);
    	}
    	Node* copy(Node* root)
    	{
    		if (root == nullptr)
    			return nullptr;
    		Node* newroot = new Node(root->_val);
    		newroot->_left = copy(root->_left);
    		newroot->_right = copy(root->_right);
    		return newroot;
    	}
    
    	~BSTree()
    	{
    		Destroy();
    	}
    	void Destroy()
    	{
    		return _Destroy(_root);
    	}
    	void _Destroy(Node* root)
    	{
    		if (root == nullptr)
    			return;
    		_Destroy(root->_left);
    		_Destroy(root->_right);
    		delete root;
    	}
    
    	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 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->_left=cur;
    		}
    		else
    		{
    			parent->_right=cur;
    		}
    		return true;
    	}
    	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 (cur == _root)
    					{
    						_root = cur->_right;
    					}
    					else
    					{
    						if (cur == parent->_left)//我是父亲的左
    						{
    							parent->_left = cur->_right;
    						}
    						else//我是父亲的左
    						{
    							parent->_right = cur->_right;
    						}
    					}
    					delete cur;
    					return true;
    				}
    				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;
    						}
    					}
    					delete cur;
    					return true;
    				}
    				else//第二种(俩孩子)替换删除法
    				{
    					Node* rightMinparent = cur;
    					Node* rightMin = cur->_right;
    					while (cur->_right)
    					{
    						rightMinparent = rightMin;
    						rightMin = rightMin->_left;
    					}
    					if (rightMinparent->_left == rightMin)
    					{
    						rightMinparent->_left = rightMin->_right;
    					}
    					else
    					{
    						rightMinparent->_right = rightMin->_right;
    					}
    				}
    			}
    		}
    		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;
    };
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
  • 相关阅读:
    glib g_signal_new 参数用法
    nginx空字节漏洞复现
    离线方式安装高可用RKE2 (版本: v1.22.13+rke2r1)记录
    拓扑排序小结
    kafka和flink的入门到精通 2 系统架构,实时数仓架构,Kafka
    看看谁还在看目标检测算法
    《最新出炉》系列入门篇-Python+Playwright自动化测试-41-录制视频
    unicode/utf8/utf16/utf32笔记
    b站老王 自动驾驶决策规划学习记录(十二)
    axios+vite配置反向代理踩坑记录
  • 原文地址:https://blog.csdn.net/m0_73802195/article/details/137402184