目录
其又称二叉排序树、二叉查找树。
满足以下性质:
非空左子树的key值小于根结点的key值;
非空右子树的key值大于根结点的key值;
左、右子树都是二叉搜索树。
在树中,寻找适合key插入的位置,找到插入返回true ,已有重复key返回false.
思路:
比较key 和根结点的_key 如果key大于_key 往根的右子树去找
如果key小于_key 往根的左子树去找
如果树中不存在已有的key 则必定走到空结点 ,链接上key的值即可
如果树中存在key 结束查找 返回false
画图辅助理解

在树中,插入key为五的结点:
定义一个cur临时结点
1--比较cur->_key 与key(下面简称比较) key小于cur->_key 往左子树中查找 cur=cur->left
2--比较 key>cur->_key 往右子树查找 cur=cur->right
3--比较 key
4--比较 key>cur->_key 往右子树查找 cur=cur->right
5--cur为空 找到可以链接的位置 链接上结点

以下有俩点需要注意:
1.如果树为空 ,则需要先创建树
2.链接结点,需要保存cur的父亲
下面代码演示
- bool Insert(const K& key)
- {
- if (_root == nullptr)
- {
- _root = new Node(key);
- return true;
- }
-
- //找到适合插入的位置
- Node* cur = _root;
- Node* parent = nullptr;
- while (cur)
- {
- parent = cur; //更新parent
- if (cur->_key > key)
- {
- cur = cur->_left;
- }
- else if (cur->_key < key) //更新parent
- {
- cur = cur->_right;
- }
- else
- return false;
- }
-
- //准备插入 插入点一定是空结点
- cur = new Node(key);
-
- //判断插入父亲的左还是右
- if (parent->_key > key) parent->_left = cur;
- else parent->_right = cur;
-
- //插入成功
- return true;
-
- }
关于二叉搜索树的删除相对不容易,需要考虑多种情况,下面就来详细解释。

上图列举四种具有代表性的删除结点
分别是:
key结点的左和右子树都为空 key的左子树为空 key的右子树为空 key的左和右子树都不为空
对于左子树右子树都为空 我们可以归于左子树或者右子树为空的一种中
下文 只需要对 2 3 4三种方式进行剖析
这三种方式都必须先找到cur
找cur的方式与inser相同 ,在此不做介绍
1)cur的左为空
--1--判断cur是否为根结点 是则将cur->left赋值给_root

--2--判断cur是父亲的左还是右 ,链接cur->right

2)cur的右为空
该方式与1)类似
需要判断cur是否为root结点
不为root则需要判断是父亲的左还是右结点
在此不做过多的介绍
3)cur的左和右子树都不为空
如果我们简单的删除cur ,则破坏搜索二叉树,因此我们引入替换法
在cur的子树中,找到一个合适的值替换,使树仍为搜索二叉树
找替换结点的方式:
左子树中,找最大
右子树中,找最小

如果我们要删除key为3的结点 因为cur的结点有左子树和右子树 所以需要找替换
我们找cur右子树的最小值 即右子树最左边的元素 4
交换 3 和 4的值 然后删除 rightmin的结点
要删除rightmin结点 需要注意该结点的右子树需要被链接到cur的父亲上
代码剖析
- bool Erase(const K& key)
- {
- //1.k的左为空
- //2.k的右为空
- //3.k左右都存在,替换删除
-
- Node* cur = _root;
- Node* parent = nullptr;
- while (cur)
- {
- parent = cur; //更新父亲
- if (cur->_key < key) cur = cur->_right;
- else if (cur->_key > key) cur = cur->_left;
-
- //找到了
- else
- {
- //1.左为空
- if (cur->_left == nullptr)
- {
-
- //删除点为根
- if (cur == _root) _root = cur->_right;
-
- //判断是父亲的左、右链接
- if (parent->_right == cur) //右链接
- {
- parent->_right = cur->_right;
- }
- else
- {
- parent->_left = cur->_right;
- }
-
- }
-
- //2.右为空
- else if (cur->_right == nullptr)
- {
- //删除点为根
- if (cur == _root) _root = cur->_left;
-
- //判断是父亲的左、右链接
- if (parent->_right == cur) //右链接
- {
- parent->_right = cur->_left;
- }
- else
- {
- parent->_left = cur->_left;
- }
- }
-
- //3.替换
- else
- {
- //找右数的最小 右子树的最左边元素
- Node* pminright = cur;
- Node* minright = cur->_right;
-
- //找到左树空就找到了
- while (minright->_left)
- {
- pminright = minright; //保存父亲结点
- minright = minright->_right;
- }
-
- cur->_key = minright->_key; //交换
-
- //判断是父亲的左子树还是右子树要被删除
- if (pminright->_left ==minright )
- pminright->_left = minright->_right;
- else
- pminright->_right= minright->_right;
- }
- }
- }
- delete(cur);
- return true;
- }
规则:左子树 ——根结点——右子树

遍历顺序 1 3 4 6 7 8 10 13 14
搜索二叉树的中序遍历可以将key按照大小遍历出
对于一个类中,我们调用InOder函数 是无法传递私有变量_root
因为写一个子函数提供遍历
- void InOrder()
- {
- _InOrder(_root);
- }
- void _InOrder(Node* root)
- {
- if (root == nullptr) return;
-
- _InOrder(root->_left);
- cout << root->_key << ":" << root->_val << endl;
- _InOrder(root->_right);
-
- }
查找树中是否存在值为key的函数,找到返回结点指针,找不到返回空
思路是从根结点开始 ,如果key 小于cur->_key 那么往左子树查找,大于则往右子树查找
- 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; //没找到,返回空指针
- }
关于递归的实现,其思路较简单 方法与循坏类似
写代码有一条不成文的准则:
能用循坏,尽量不用递归,避免栈溢出;
对于二叉搜索树的递归 需要注意:
--1--递归要传入结点参数,要写子函数调用
--2--cur的链接,不需要保存父亲结点 引用cur的指针
展示key_value模型应用的代码
- #include<iostream>
- #include<string>
- using namespace std;
-
-
- namespace key_val
- {
- template<class K, class V> //key和value
- //node
- struct BSTreeNode
- {
- BSTreeNode<K, V>* _left;
- BSTreeNode<K, V>* _right;
- K _key;
- V _val;
-
- BSTreeNode(const K& key, const V& val)
- :_left(nullptr),
- _right(nullptr),
- _key(key),
- _val(val)
- {}
- };
-
- 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* cur = _root;
- Node* parent = nullptr;
- while (cur)
- {
- parent = cur; //更新parent
- if (cur->_key > key)
- {
- cur = cur->_left;
- }
- else if (cur->_key < key) //更新parent
- {
- cur = cur->_right;
- }
- else
- return false;
- }
-
- //准备插入 插入点一定是空结点
- cur = new Node(key, value);
-
- //判断插入父亲的左还是右
- if (parent->_key > key) parent->_left = cur;
- else parent->_right = 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)
- {
- //1.k的左为空
- //2.k的右为空
- //3.k左右都存在,替换删除
-
- Node* cur = _root;
- Node* parent = nullptr;
- while (cur)
- {
- parent = cur; //更新父亲
- if (cur->_key < key) cur = cur->_right;
- else if (cur->_key > key) cur = cur->_left;
-
- //找到了
- else
- {
- //1.左为空
- if (cur->_left == nullptr)
- {
-
- //删除点为根
- if (cur == _root) _root = cur->_right;
-
- //判断是父亲的左、右链接
- if (parent->_right == cur) //右链接
- {
- parent->_right = cur->_right;
- }
- else
- {
- parent->_left = cur->_right;
- }
-
- }
-
- //2.右为空
- else if (cur->_right == nullptr)
- {
- //删除点为根
- if (cur == _root) _root = cur->_left;
-
- //判断是父亲的左、右链接
- if (parent->_right == cur) //右链接
- {
- parent->_right = cur->_left;
- }
- else
- {
- parent->_left = cur->_left;
- }
- }
-
- //3.替换
- else
- {
- //找右数的最小 右子树的最左边元素
- Node* pminright = cur;
- Node* minright = cur->_right;
-
- //找到左树空就找到了
- while (minright->_left)
- {
- pminright = minright; //保存父亲结点
- minright = minright->_right;
- }
-
- cur->_key = minright->_key; //交换
-
- //判断是父亲的左子树还是右子树要被删除
- if (pminright->_left ==minright )
- pminright->_left = minright->_right;
- else
- pminright->_right= minright->_right;
- }
- }
- }
- delete(cur);
- return true;
- }
-
- void InOrder()
- {
- _InOrder(_root);
- }
- void _InOrder(Node* root)
- {
- if (root == nullptr) return;
-
- _InOrder(root->_left);
- cout << root->_key << ":" << root->_val << endl;
- _InOrder(root->_right);
-
- }
-
- private:
- Node* _root = nullptr;
-
-
- };
本文重点学习搜索二叉树的插入和删除,它具有快速查找的功能,在查找具有O(logN)-O(N)的效率,关于搜索二叉树的实现,有较多的细节,也要仔细甄别。