二叉查找树BinarySearchTree(BST)类模板的实现
一、基础理论
1.树
- 没有儿子的节点称为树叶,具有相同父亲的节点为兄弟。
- 对任意节点ki,ki的深度为从根到k的唯一路径长,即根的深度为0。节点k的高是从k到一片树叶的最长路径的长。
- 一个结点拥有孩子的个数称为该结点的度,树中各结点度的最大值称为该树的度。
- 一棵树是N个节点和N-1条边的集合
2.二叉树
重要性质:
-
二叉树中,第 i 层最多有 2i-1 个结点。
-
如果二叉树的深度为 K,那么此二叉树最多有 2K-1 个结点。
-
二叉树中,终端结点数(叶子结点数)为 n0,度为 2 的结点数为 n2,则 n0=n2+1。
证明: -
每一棵具有N个节点的二叉树都需要N+1个nullptr链。
证明:每棵二叉树共有2N个链域,其中除根节点外每个节点都有父节点,即共有N-1个指针指向某个节点,空指针为2N-(N-1)=N+1。 -
如果二叉树中除了叶子结点,每个结点的度都为 2,则此二叉树称为满二叉树。
-
满二叉树中第 i 层的节点数为 2n-1 个。
-
具有 n 个节点的满二叉树的深度为 log2(n+1)。
3. 完全二叉树
如果二叉树中除去最后一层节点为满二叉树,且最后一层的结点依次从左到右分布,则此二叉树被称为完全二叉树。
- n 个结点的完全二叉树的深度为 ⌊log2n⌋+1。
- 对于任意一个完全二叉树来说,如果将含有的结点按照层次从左到右依次标号(如图 a)完全二叉树),对于任意一个结点 i ,完全二叉树还有以下几个结论成立:
- 当 i>1 时,父亲结点为结点 [i/2] 。(i=1 时,表示的是根结点,无父亲结点)
- 如果 2i>n(总结点的个数) ,则结点 i 肯定没有左孩子(为叶子结点);否则其左孩子是结点 2i 。
- 如果 2i+1>n ,则结点 i 肯定没有右孩子;否则右孩子是结点 2i+1 。
4.二叉查找树
使二叉树成为二叉查找树的性质是,对于树中的每个节点X,它的左子树中所有项的值均小于X中的项,而它的右子树中所有项的值均大于X中的项。如:
这样,在有n个元素的该数据结构中查找一个元素的时间复杂度为log2n。
二、 代码实现
#include<ostream> using namespace std; template<typename Comparable> class BinarySearchTree { private: struct BinaryNode { Comparable element; BinaryNode* left; BinaryNode* right; BinaryNode(const Comparable&theElement,BinaryNode*lt,BinaryNode*rt) :element{theElement},left{lt},right{rt}{} BinaryNode(Comparable&&theElement,BinaryNode*lt,BinaryNode*rt) :element{std::move(theElement)},left{lt},right{rt}{} }; BinaryNode* root; /** * 向子树插入元素的内部方法 * x是要插入的项 * t为该子树的根节点 * 置子树的新根 **/ void insert(const Comparable& x, BinaryNode*& t) { if (t == nullptr) t = new BinaryNode{ x,nullptr,nullptr }; else if (x < t->element) insert(x, t->left); else if (t->element < x) insert(x, t->right); else ; //重复元,什么也不做 } /** * 向子树插入元素的内部方法 * x是通过移动实现要插入的项 * t为该子树的根节点 * 置子树的新根 */ void insert(Comparable&& x, BinaryNode*& t) { if (t == nullptr) t = new BinaryNode{ std::move(x),nullptr,nullptr }; else if (x < t->element) insert(std::move(x), t->left); else if (x > t->element) insert(std::move(x), t->right); else ; //重复元,什么也不做 } /** * 从一棵子树删除一项的内部方法 * 参数x是要被删除的项 * 参数t为该子树的根节点 * 置该子树的新的根 */ void remove(const Comparable& x, BinaryNode*& t) { if (t == nullptr) return; //项没找到,什么也不做 if (x < t->element) remove(x, t->left); else if (t->element < x) remove(x, t->right); else if (t->left != nullptr && t->right != nullptr) //有两个儿子 { t->element = findMin(t->right)->element; remove(t->element, t->right); } else { BinaryNode* oldNode = t; t = (t->left != nullptr) ? t->left : t->right; delete oldNode; } } /** * 找出子树t中最小项的内部方法 * 返回包含最小项的节点 * 递归实现 */ BinaryNode* findMin(BinaryNode* t) const { if (t == nullptr) return nullptr; if (t->left == nullptr) return t; return findMin(t->left); } /** * 找出子树t中最大项的内部方法 * 返回包含最大项的节点 * 非递归实现 */ BinaryNode* findMax(BinaryNode* t) const { if (t != nullptr) { while (t->right != nullptr) t = t->right; } return t; } /** * 测试一项是否在子树上的内部方法 * x是要查找的项 * t是作为该子树的根的节点 */ bool contains(const Comparable& x, BinaryNode* t) const { if (t == nullptr) return false; else if (t->element < x) { //x比当前节点大,去右子树找 return contains(x, t->right); } else if (t->element > x) { //x比当前节点小,去左子树找 return contains(x, t->left); } else return true; //匹配,找到了 } /** * 使子树为空的内部方法 */ void makeEmpty(BinaryNode*& t) { if (t != nullptr) { makeEmpty(t->left); makeEmpty(t->right); delete t; } t = nullptr; } //以排序顺序打印根在t处的子树的内部方法 void printTree(BinaryNode* t, ostream& out)const { if (t != nullptr) { printTree(t->left, out); out << t->element << endl; printTree(t->right, out); } } /** * 克隆子树的内部方法 */ BinaryNode* clone(BinaryNode* t)const { if (t == nullptr) return nullptr; else return new BinaryNode{ t->element,clone(t->left),clone(t->right) }; } /** * 计算根在t处子树的高度的内部方法 * 后序遍历计算树的高度 */ int height(BinaryNode* t)const { if (t == nullptr) { return -1; } else return 1 + max(height(t->left), height(t->right)); } public: BinarySearchTree() :root{ nullptr } {} BinarySearchTree(const BinarySearchTree& rhs) :root{ nullptr } { root = clone(rhs.root); } BinarySearchTree(BinarySearchTree&& rhs) :root{ rhs.root } { rhs.root = nullptr; } ~BinarySearchTree() { makeEmpty(); } const Comparable& findMin()const { return findMin(root)->element; } const Comparable& findMax()const { return findMax(root)->element; } //如果在树中找到x,则返回true bool contains(const Comparable& x)const { return contains(x, root); } bool isEmpty()const { return root == nullptr; } int height()const { return height(root); } //按排列顺序打印树的内容 void printTree(ostream& out)const { if (isEmpty()) out << "Empty tree" << std::endl; else printTree(root, out); } void makeEmpty() { makeEmpty(root); } //将x插入到树中,忽略重复元 void insert(const Comparable& x) { insert(x, root); } void insert(Comparable&& x) { insert(std::move(x), root); } //将x从树中删除,若没找到x,则什么也不做 void remove(const Comparable& x) { remove(x, root); } /** * Copy assignment */ BinarySearchTree& operator=(const BinarySearchTree& rhs) { BinarySearchTree copy = rhs; std::swap(*this, copy); return *this; } /** * Move assignment */ BinarySearchTree& operator=(BinarySearchTree&& rhs) { std::swap(root, rhs.root); return *this; } };
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战