目录
二叉搜索树,若其不是空树测具有以下性质:
因其中序遍历的结果是有序的,故也称为二叉排序树

使用模板建树,泛化使用范围。
- namespace key
- {
- template<class K>
- struct BSTreeNode
- {
- BSTreeNode
* _left; - BSTreeNode
* _right; - K _key;
-
- BSTreeNode(const K& key)
- :_left(nullptr)
- , _right(nullptr)
- , _key(key)
- { }
- };
-
- template<class K>
- class BSTree
- {
- typedef BSTreeNode
Node; -
- private:
- Node* _root = nullptr;
- }
- }
思路:因为二叉搜索树满足如上性质,所以对于每个节点, 其左子树都比它小, 右子树都比它大。所以查找元素比当前节点值大就往右走,比当前节点值小就往左走。
- 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;
- }
思路:如果是空树的话直接赋值给root建树,当插入元素比当前节点值小时,往左走; 当插入元素比当前节点值大时,往右走。因其为无向图,我们应加入一个parent节点来记录当前节点(cur节点)前的位置。
注意:
1.因为二叉搜索树中不能有重复值,所以注意判重。
2.插入元素时不需要挪动其他节点,遍历到底即可。

- 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;
- }
删除元素是二叉搜索树的难点 观察开头给出的例图

不难若删除一个节点 其有三种情况(删除的节点左右都为空的情况涵盖在了前两种)
1.删除的节点左子树为空 如图中值为1的节点
2.删除的节点右子树为空 如图中值为6的节点
3.删除的节点左右子树都不为空 如图中值为14、3的节点
下面对以上情况逐一进行讨论
对于每个节点,其左子树都小于自己,其右子树都大于自己。
1.若删除的节点的左子树为空,则使其父节点的指向该节点的 right,若该节点位于其父节点的左侧,则使父节点的left 指向该节点的 right,若在右侧,则使父节点的right 指向该节点的 right。
2.若删除的节点的右子树为空,则使其父节点的指向该节点的 left,若该节点位于其父节点的左侧,则使父节点的left 指向该节点的 left,若在右侧,则使父节点的right 指向该节点的 left。
3.若删除的节点左右子树都不为空
采用替换法删除:
基于搜索二叉树的性质 我们需要找到右子树的最小节点或左子树的最大节点来替换要删除的节点,再将这个找到的节点的子树继承给其父节点,即保证删除后仍然满足对于每个节点,其左子树都小于自己,其右子树都大于自己。



注意:
若删除的节点类似元素8, 其右子树
的最小节点就是其右节点,需要特判。
- bool Erase(const K& key)
- {
- //删除有三种情况
- //1 删除的节点的左为空
- //2 删除的节点的右为空
- //3 删除的节点左右都空 ---> 替换删除法
- //即找左子树的最右节点(最大值) 或 右子树的最左节点代替即可
- //下即 右子树的最左节点
- 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->_right;
- }
- else
- {
- //相等删除
- //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;
- }
- //2 右为空 父亲指向我的左
- 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;
- }
- //3 左右都不为空 找右子树的最小值替换
- else
- {
- Node* RightMinParent = cur;
- Node* RightMin = cur->_right;
- while (RightMin->_left)
- {
- RightMinParent = RightMin;
- RightMin = RightMin->_left;
- }
-
- swap(cur->_key, RightMin->_key);
- //两种情况
- //1.一开始的rightmin 有左子树 把他的右子树给父亲的左边
- //2.一开始的rightmin 没有左子树
-
- if (RightMinParent->_left == RightMin)
- {
- RightMinParent->_left = RightMin->_right;
-
- }
- else
- {
- RightMinParent->_right = RightMin->_right;
- }
-
- delete RightMin;
- }
-
- //成功删除
- return true;
- }
- }
- return false;
- }
中序遍历按 左子树 根 右子树 的循序走即可
- void _InOrder(Node* root)
- {
- if (root == nullptr)
- return;
-
- _InOrder(root->_left);
- cout << root->_key << ' ';
- _InOrder(root->_right);
- }
1.K模型:K模型即只有key作为关键码,结构中只需要存储Key即可,关键码即为需要搜索到
的值。
比如:给一个单词word,判断该单词是否拼写正确,具体方式如下
2.KV模型:每一个关键码key,都有与之对应的值Value,即
式在现实生活中非常常见:
- namespace key
- {
- //BS节点
- template<class K>
- struct BSTreeNode
- {
- BSTreeNode
* _left; - BSTreeNode
* _right; - K _key;
-
- BSTreeNode(const K& key)
- :_left(nullptr)
- , _right(nullptr)
- , _key(key)
- { }
- };
-
- template<class K>
- class BSTree
- {
- typedef BSTreeNode
Node; -
- 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)
- {
- //删除有三种情况
- //1 删除的节点的左为空
- //2 删除的节点的右为空
- //3 删除的节点左右都空 ---> 替换删除法
- //即找左子树的最右节点(最大值) 或 右子树的最左节点代替即可
- //下即 右子树的最左节点
- 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->_right;
- }
- else
- {
- //相等删除
- //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;
- }
- //2 右为空 父亲指向我的左
- 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;
- }
- //3 左右都不为空 找右子树的最小值替换
- else
- {
- Node* RightMinParent = cur;
- Node* RightMin = cur->_right;
- while (RightMin->_left)
- {
- RightMinParent = RightMin;
- RightMin = RightMin->_left;
- }
-
- swap(cur->_key, RightMin->_key);
- //两种情况
- //1.一开始的rightmin 有左子树 把他的右子树给父亲的左边
- //2.一开始的rightmin 没有左子树
-
- if (RightMinParent->_left == RightMin)
- {
- 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);
- }
- Node* _root = nullptr;
- };
-
- };
kv模型笔者不再赘述,原理相同,请读者自行理解。
- namespace key_value
- {
- //BS节点
- template<class K, class V>
- struct BSTreeNode
- {
- BSTreeNode
* _left; - BSTreeNode
* _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
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 false;
- }
- }
-
- //到底部之后
- cur = new Node(key, value);
- if (parent->_key < key)
- {
- parent->_right = cur;
- }
- else
- {
- parent->_left = cur;
- }
-
- return true;
- }
- // find 改为返回节点
- 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 cur;
- }
-
- bool Erase(const K& key)
- {
- //删除有三种情况
- //1 删除的节点的左为空
- //2 删除的节点的右为空
- //3 删除的节点左右都空 ---> 替换删除法
- //即找左子树的最右节点(最大值) 或 右子树的最左节点代替即可
- //下即 右子树的最左节点
- 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->_right;
- }
- else
- {
- //相等删除
- //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;
- }
- //2 右为空 父亲指向我的左
- 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;
- }
- //3 左右都不为空 找右子树的最小值替换
- else
- {
- Node* RightMinParent = cur;
- Node* RightMin = cur->_right;
- while (RightMin->_left)
- {
- RightMinParent = RightMin;
- RightMin = RightMin->_left;
- }
-
- swap(cur->_key, RightMin->_key);
- //两种情况
- //1.一开始的rightmin 有左子树 把他的右子树给父亲的左边
- //2.一开始的rightmin 没有左子树
-
- if (RightMinParent->_left == RightMin)
- {
- 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 << ":" << root->_value << endl;
- _InOrder(root->_right);
- }
-
- Node* _root = nullptr;
- };
-
-
-
- };
最后留下三组测试函数:
k模型
- void TestBSTree1()
- {
- int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };
- BSTree<int> t1;
- for (auto e : a)
- {
- t1.Insert(e);
- }
-
- t1.InOrder();
-
- cout << endl;
- //t1.Erase(3);
- t1.Erase(8);
-
- t1.InOrder();
-
- for (auto e : a)
- {
- t1.Erase(e);
- t1.InOrder();
- }
- }
kv模型
-
-
- void TestBSTree2()
- {
- BSTree
dict; - dict.Insert("string", "字符串");
- dict.Insert("left", "左边");
- dict.Insert("insert", "插入");
- //...
-
- string str;
- while (cin >> str)
- {
- BSTreeNode
* ret = dict.Find(str); - if (ret)
- {
- cout << ret->_value << endl;
- }
- else
- {
- cout << "无此单词,请重新输入" << endl;
- }
- }
- }
-
-
- void TestBSTree3()
- {
- // 统计次数
- string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜",
- "苹果", "香蕉", "苹果", "香蕉","苹果","草莓", "苹果","草莓" };
- BSTree
int> countTree; - for (const auto& str : arr)
- {
- auto ret = countTree.Find(str);
- if (ret == nullptr)
- {
- countTree.Insert(str, 1);
- }
- else
- {
- ret->_value++;
- }
- }
-
- countTree.InOrder();
- }