赞
踩
二叉搜索树,也叫二叉排序树,是一种具有特殊性质的二叉树。
若左子树不空,则左子树上所有节点的值都小于根节点的值。
若右子树不空,则右子树上所有节点的值都大于根节点的值。
任意节点的子树也都是二叉搜索树。
如下面这颗二叉树:
我们可以看出这个二叉树符合上面的规则。
并且二叉搜索树还有一个重要的特点:
二叉搜索树的中序遍历,如果排序的是数字的话,那么他的中序遍历结果一定是个有序的数组,上面这个树的中序遍历为[1 15 16 17 18 20 23 24 27 28 30].
概念:
key的模型是指每个节点只有一个键值,用于确定节点在树中的位置。节点的键值必须满足二叉搜索树的性质,即左子节点的键值小于父节点的键值,右子节点的键值大于父节点的键值。这种模型比较简单,但是不能存储额外的信息。
key/value模型是指每个节点有一个键值和一个数据值,键值用于确定节点在树中的位置,数据值用于存储节点的附加信息。节点的键值仍然必须满足二叉搜索树的性质,但是数据值可以是任意类型或对象。这种模型比较灵活,可以实现一些高级功能,比如映射或字典。
一般而言,我们在程序中使用的都是key-value模型的二叉搜索树,因为key模型的没什么实际作用。
假设你想要存储图书的话,在key/value模型中就可以让图书姓名当作键值,数量当作数据值,以此来详细统计一下图书。
但是我们先由易到难,先来讲解一下key模型的二叉搜索树。
无论是哪种模型,我们都需要定义一个内部类来表示节点,根据树的基本含义我们可以得知,除了key模型所需要的一个元素之外,我们还需要左右两个指针来表示每个节点的左孩子右孩子。
故得出:
- template<typename K>
- class BSTreeNode //二叉搜索树的节点类
- {
- public:
- BSTreeNode(const K& key = K())
- :_left(nullptr)
- ,_right(nullptr)
- ,_key(key)
- {}
- BSTreeNode<K>* _left;
- BSTreeNode<K>* _right;
- K _key;
- };
在整个二叉树的类中,我们只要保证有一个头节点就好了,因为他会指向自己的左右孩子。
故得出:
- template<class K>
- class BSTree
- {
- typedef BSTreeNode<K> Node; //typedef一下节省代码量
- private:
- Node* _root = nullptr;
- };
二叉搜索树的插入其实只需要注意一点,那就是,插入后,仍然保持二叉搜索树的性质。
那么我们就要与原有的树上的值进行比较,到了合适的位置后再进行插入。
比如说这张图:
如果我们要插入的值为 19 的话,需要进行如下几段比较
因此,平衡二叉树的插入实际上就是一个 比较大小 直到寻找到空位置 再进行插入的过程 。
但是,如果插入的元素在数中已经有了该怎么办呢?
平衡二叉树的key模型基本上是拒绝重复元素的插入的。(难办就别办了)
- bool Insert(const K& key)
- {
- //特殊情况,如果头节点为nullptr,直接插入
- 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;
-
- }

可以说,二叉搜索树在查找上面的进步是很大,他的平均查找速度为O(logN),最坏查找情况为O(N)。(N为树的高度)
为什么呢,其实类似于二分查找,它每次在查找的时候都只会前往左右区间,也就是直接剔除一半的值,最后便得出平均查找速度。
最坏查找是因为有特殊情况,(一般是有序)使二叉树退化成单子树,从而大幅降低了查找效率。
如:
其实查找也是个比较大小的思想,通过一次一次的比较大小,最终得出结果。
如:
我们要查找 18
- Node* Find(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
- {
- //与插入不同,查找到返回节点
- return cur;
- }
- }
- //没有查找到 返回空
- return nullptr;
- }

二叉搜索树的删除和插入一样,都需要保持一个原则,插入后,仍然保持二叉搜索树的性质。
二叉搜索树的删除无非就四种情况:
1.删除没有孩子节点的父节点
2.删除没有左孩子,但有右孩子的父节点。
3.删除没有有孩子,但有左孩子的节父点。
4.删除有两个孩子的父节点
还是经典老图,稍微改一下,我们分别要删除16 15 28 24这四个节点,分别对应四种情况,我们来看一下。
其实第一种情况可以归类为2.3种情况
- bool Erase(const K& key)
- {
-
- //父指针记录 cur指针查找
- Node* parent = nullptr;
- Node* cur = _root;
- //while循环遍历查找
- while (cur)
- {
- if (cur->_key < key)
- {
- parent = cur;
- cur = cur->_right;
- }
- else if (cur->_key > key)
- {
- parent = cur;
- cur = cur->_left;
- }
- else
- {
- //查找到
- //开始删除
- //一共有三种情况
-
- //待删除节点左指针为nullptr
- if (cur->_left == nullptr) //第一二种 情况合起来
- {
- //处理特殊情况 如果要删除的是头指针
- if (_root == cur)
- {
- _root = cur->_right;
- }
- else
- {
- //如果是节点左右都为nullptr 父节点的指针就指向nullptr
- if (parent->_left == cur)
- {
- parent->_left = cur->_right;
- }
- else
- {
- parent->_left = cur->_right;
- }
- }
- delete cur;
- cur = nullptr;
- }
- //待删除节点右指针为nullptr
- else if (cur->_right == nullptr)
- {
- //处理特殊情况 如果要删除的是头指针
- if (_root == cur)
- {
- _root = cur->_left;
- }
- else
- {
- if (parent->_right == cur)
- {
- parent->_right = cur->_left;
- }
- else
- {
- parent->_right = cur->_left;
- }
- }
- delete cur;
- cur = nullptr;
- }
-
- //待删除节点左右孩子都有
- else
- {
- //查找左子树的最大值 其实就是查找cur左子树的最右节点
- /*Node* maxParent = cur;
- Node* max = cur->_left;
- while (max->_right)
- {
- maxParent = max;
- max =max->_left;
- }*/
-
- Node* minParent = cur;
- Node* min = cur->_right;
- //查找右子树的最小值 其实就是查找cur右子树的最左节点
- while (min->_left)
- {
-
- minParent = min;
- min = min->_left;
- }
- //将找到的值和cur交换
- swap(cur->_key, min->_key);
-
- //如果查找到的最大值或者最小值 还有另外的节点 需要链接
- if (minParent->_left == min)
- minParent->_left = min->_right;
- else
- minParent->_right = min->_right;
-
-
- delete min;
- min = nullptr;
- }
- return true;
- }
- }
- //没有删除返回false
- return false;
- }

没啥可说的 普通二叉树的遍历罢了,除了中序遍历可能是一个有序数组外这一个性质外。
- void _InOrder(Node* root)
- {
- if (root == nullptr)
- {
- return;
- }
- _InOrder(root->_left);
- cout << root->_key << endl;
- _InOrder(root->_right);
-
- }
- template<typename K>
- class BSTreeNode //二叉搜索树的节点类
- {
- public:
- BSTreeNode(const K& key = K())
- :_left(nullptr)
- ,_right(nullptr)
- ,_key(key)
- {}
- BSTreeNode<K>* _left;
- BSTreeNode<K>* _right;
- K _key;
- };
- template<class K>
- class BSTree
- {
- typedef BSTreeNode<K> Node; //typedef一下节省代码量
- public:
- bool Insert(const K& key)
- {
- //特殊情况,如果头节点为nullptr,直接插入
- 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;
-
- }
- Node* Find(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
- {
- //与插入不同,查找到返回节点
- return cur;
- }
- }
- //没有查找到 返回空
- return nullptr;
- }
- bool Erase(const K& key)
- {
-
- //父指针记录 cur指针查找
- Node* parent = nullptr;
- Node* cur = _root;
- //while循环遍历查找
- while (cur)
- {
- if (cur->_key < key)
- {
- parent = cur;
- cur = cur->_right;
- }
- else if (cur->_key > key)
- {
- parent = cur;
- cur = cur->_left;
- }
- else
- {
- //查找到
- //开始删除
- //一共有三种情况
-
- //待删除节点左指针为nullptr
- if (cur->_left == nullptr) //第一二种 情况合起来
- {
- //处理特殊情况 如果要删除的是头指针
- if (_root == cur)
- {
- _root = cur->_right;
- }
- else
- {
- //如果是节点左右都为nullptr 父节点的指针就指向nullptr
- if (parent->_left == cur)
- {
- parent->_left = cur->_right;
- }
- else
- {
- parent->_left = cur->_right;
- }
- }
- delete cur;
- cur = nullptr;
- }
- //待删除节点右指针为nullptr
- else if (cur->_right == nullptr)
- {
- //处理特殊情况 如果要删除的是头指针
- if (_root == cur)
- {
- _root = cur->_left;
- }
- else
- {
- if (parent->_right == cur)
- {
- parent->_right = cur->_left;
- }
- else
- {
- parent->_right = cur->_left;
- }
- }
- delete cur;
- cur = nullptr;
- }
-
- //待删除节点左右孩子都有
- else
- {
- //查找左子树的最大值 其实就是查找cur左子树的最右节点
- /*Node* maxParent = cur;
- Node* max = cur->_left;
- while (max->_right)
- {
- maxParent = max;
- max =max->_left;
- }*/
-
- Node* minParent = cur;
- Node* min = cur->_right;
- //查找右子树的最小值 其实就是查找cur右子树的最左节点
- while (min->_left)
- {
-
- minParent = min;
- min = min->_left;
- }
- //将找到的值和cur交换
- swap(cur->_key, min->_key);
-
- //如果查找到的最大值或者最小值 还有另外的节点 需要链接
- if (minParent->_left == min)
- minParent->_left = min->_right;
- else
- minParent->_right = min->_right;
-
-
- delete min;
- min = nullptr;
- }
- return true;
- }
- }
- //没有删除返回false
- return false;
- }
-
- void InOrder()
- {
- _InOrder(_root);
- cout << endl;
- }//为了预防数据被改,我们改成privat
- private:
- void _InOrder(Node* root)
- {
- if (root == nullptr)
- {
- return;
- }
- _InOrder(root->_left);
- cout << root->_key << endl;
- _InOrder(root->_right);
-
- }
- Node* _root = nullptr;
- };

如果说key模型的值只能表示在不在的话,那么key-value模型就可以表示通过一个值查找另一个值。
key value模型 与上述实现的二叉搜索树实现功能差不多,只是增加了一个模板参数value。
代码为:
- template<class K, class V>
- class BSTreeNode
- {
- public:
- BSTreeNode(const K& key=K(),const V& val=V())
- :_left(nullptr)
- ,_right(nullptr)
- ,_key(key)
- ,_val(val)
- {
- }
- BSTreeNode<K, V>* _left;
- BSTreeNode<K, V>* _right;
- K _key;
- V _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* 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;
-
- }
- Node* Find(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
- {
- return cur;
- }
- }
- return nullptr;
- }
- 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 (_root == cur)
- {
- _root = cur->_right;
- }
- else
- {
- if (parent->_left == cur)
- {
- parent->_left = cur->_right;
- }
- else
- {
- parent->_left = cur->_right;
- }
- }
- delete cur;
- cur = nullptr;
- }
- else if (cur->_right == nullptr)
- {
- if (_root == cur)
- {
- _root = cur->_left;
- }
- else
- {
- if (parent->_right == cur)
- {
- parent->_right = cur->_left;
- }
- else
- {
- parent->_right = cur->_left;
- }
- }
- delete cur;
- cur = nullptr;
- }
- else
- {
-
- Node* minParent = cur;
- Node* min = cur->_right;
-
- while (min->_left)
- {
-
- minParent = min;
- min = min->_left;
- }
- swap(cur->_key, min->_key);
-
- if (minParent->_left == min)
- minParent->_left = min->_right;
- else
- minParent->_right = min->_right;
- delete min;
- min = nullptr;
- }
- 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->_val<< endl;
- _InOrder(root->_right);
-
- }
- Node* _root = nullptr;
- };

Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。