数据结构——二叉树
Posted 霜序之晦
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构——二叉树相关的知识,希望对你有一定的参考价值。
无论是向量、列表、还是栈和队列,其保存的元素之间都存在一个线性次序,故它们都属于线性结构,树则不然,其元素之间并不存在直接后继或直接前驱的关系。不过可以对树附加某种约束,从而确定某种线性次序,因此树属于半线性结构。树有着不计其数的变种,在算法理论以及实际应用中,它们都扮演着最为关键的角色。之所以如此,是因得益于其独特而又普适的逻辑结构。树是一种分层结构,而层次化这一特征几乎蕴含于所有事物及其联系当中,成为其本质属性质疑。从文件系统、互联网域名系统和数据库系统,层次化特征及层次化结构均无处不在。
树
从图论的角度看,树等价于连通无环图。因此与一般的图相同,树也由一组顶点(vertex),以及联接与其间的若干条边(edge)组成。指定某一特定顶点并称之为根(root),我们也称之为有根树。此时,从程序实现的角度,我们也称顶点为节点(node)。
深度与层次
由树的连通性,每一节点与根节点之间都有一条路径相联;而根据树的无环性,由根通往每个节点的路径必然唯一。因此沿每个节点v到根r的唯一通路所经历的边的数目,称作v的深度(depth),记作depth(v)。依据深度排序,可对所有节点做分层归类。特别地,约定树根节点的深度depth(r) = 0,故属于第0层。
祖先、后代与子树
任一节点在通往树根沿途所经过的每一个节点都是其祖先(ancestor),v是它们的后代(descendant)。特别地,v的祖先/后代包括其本身,而v以外的祖先/后代成为真祖先(proper ancestor)/真后代(proper descendent)。特别地若节点u是v的祖先且恰好比v高一层则称u是v的父亲(parent),v是u的孩子(child)。无孩子的节点称作叶节点(leaf),包含根节点在内的其他节点都称为内部节点(internal node)。v所有的后代及其之间的联边称作子树(subtree),记作subtree(v)。
高度
树T中所有节点深度的最大值称为该树的高度(height),记作height(T)。特别地,仅含单个节点的树的高度为0,空树高度为-1。
多叉树
一般地,树中各节点的孩子数目并不确定。每个节点的孩子数目均不超过k个的有根树,称作k叉树。任何有根有序的多叉树,可以等价地转换为一棵二叉树。
多叉树的“长子”+“兄弟”表示法
有序多叉树中任一非叶节点都有唯一“长子”,而且从该“长子”出发,可以按照预先约定或指定的次序遍历所有孩子节点。
尽管二叉树是多叉树的一个子集,但其对应用问题的描述刻画能力绝不低于后者。实际上得益于二叉树的简洁性以及结构的规范性,二叉树所支撑的算法往往能更好地得到描述,更加简洁地得到实现。二叉树的身影几乎出现在所有的应用领域当中,这也是一个重要的原因。
二叉树的实现
作为图的特殊形式,二叉树的基本组成单元是节点与边;作为数据结构,其基本的组成实体是二叉树节点,而边则对应于节点之间的相互引用。
二叉树节点
1 enum class RBColor 2 { 3 //红黑树节点颜色 4 RED, BlACK 5 }; 6 7 template<typename T> 8 struct TreeNode 9 { 10 T _val; //保存的值 11 TreeNode<T>* _parent; //父节点 12 TreeNode<T>* _left; //左子节点 13 TreeNode<T>* _right; //右子节点 14 int _height; //树的高度(空树高度为-1) 15 int _npl; //null path length 16 RBColor _color; //节点颜色 17 //构造函数 18 TreeNode(T e = T(), TreeNode<T>* parent = nullptr, TreeNode<T>* left = nullptr, TreeNode<T>* right = nullptr, 19 int height = 0, int npl = 1, RBColor color = RBColor::RED) 20 :_val(e), _parent(parent), _left(left), _right(right), _height(height), _npl(npl), _color(color) {} 21 //作为左子节点插入 22 void insertAsLc(T const& e) 23 { 24 assert(!_left); 25 _left = new TreeNode<T>(e, this); 26 } 27 //作为右子节点插入 28 void insertAsRc(T const& e) 29 { 30 assert(!_right); 31 this->_right = new TreeNode<T>(e, this); 32 } 33 //重载比较器、判等器 34 bool operator<(const TreeNode<T>& n) 35 { 36 return _val < n._val; 37 } 38 bool operator>(const TreeNode<T>& n) 39 { 40 return _val > n._val; 41 } 42 bool operator==(const TreeNode<T>& n) 43 { 44 return _val == n._val; 45 } 46 bool operator!=(const TreeNode<T>& n) 47 { 48 return _val != n._val; 49 } 50 //中序遍历下当前节点的直接后继 51 TreeNode<T>* succ(); 52 }; 53 54 55 template<typename T> 56 TreeNode<T>* TreeNode<T>::succ() 57 { 58 TreeNode<T>* s = this; 59 if (_right) 60 { 61 s = _right; 62 while (s->_left)s = s->_left; 63 } 64 else 65 { 66 while (s->_parent && s->_parent->_right == s)s = s->_parent; 67 s = s->_parent; 68 } 69 return s; 70 }
二叉树
1 template<typename T> 2 class BinTree 3 { 4 protected: 5 TreeNode<T>* _root; //树根节点 6 size_t _size; //树的规模 7 TreeNode<T>* _clone(TreeNode<T>* Root); //克隆一棵树 8 void _setRoot(const T& e); //设置树根节点 9 template<typename F> 10 void _travPre(TreeNode<T>* root, F&& f); //先序遍历 11 template<typename F> 12 void _travPost(TreeNode<T>* root, F&& f); //后续遍历 13 template<typename F> 14 void _travIn(TreeNode<T>* root, F&& f); //中序遍历 15 template<typename F> 16 void _travLevel(TreeNode<T>* root, F&& f); //层次遍历 17 int _remove(TreeNode<T>* rm); //删除一棵子树 18 //树的高度 19 int Height(TreeNode<T>* p) { return p ? p->_height : -1; } 20 //更新树的高度 21 int _updateHeight(TreeNode<T>* p); 22 void _updateHeightAbove(TreeNode<T>* p); 23 //判断是根节点、叶节点 24 bool _isLeaf(TreeNode<T>* p); 25 bool _isRoot(TreeNode<T>* p); 26 public: 27 //构造 28 BinTree() :_root(nullptr), _size(0) {} 29 //拷贝 30 BinTree(const BinTree<T>& Tree); 31 //重载赋值运算符 32 BinTree<T>& operator=(const BinTree<T>& Tree); 33 //析构 34 ~BinTree(); 35 size_t size()const { return _size; } 36 bool empty()const { return !_size; } 37 TreeNode<T>* root() { return _root; } 38 //e作为p的左、右孩子插入 39 void insertAsLc(TreeNode<T>* p, T const& e) 40 { 41 p->insertAsLc(e); 42 _size++; 43 } 44 void insertAsRc(TreeNode<T>* p, T const& e) 45 { 46 p->insertAsRc(e); 47 _size++; 48 } 49 //作为根节点插入 50 void insertAsRoot(const T& e) 51 { 52 _setRoot(e); 53 } 54 //树的遍历 55 template<typename F> 56 void travPre(F&& f) 57 { 58 _travPre(_root, f); 59 } 60 template<typename F> 61 void travIn(F&& f) 62 { 63 _travIn(_root, f); 64 } 65 template<typename F> 66 void travPost(F&& f) 67 { 68 _travPost(_root, f); 69 } 70 template<typename F> 71 void travLevel(F&& f) 72 { 73 _travLevel(_root, f); 74 } 75 //删除子树 76 void remove(TreeNode<T>* rm); 77 }; 78 79 template<typename T> 80 TreeNode<T>* BinTree<T>::_clone(TreeNode<T>* Root) 81 { 82 if (Root) 83 { 84 TreeNode<T>* root = new TreeNode<T>(*Root); 85 TreeNode<T>* left = _clone(Root->_left); 86 TreeNode<T>* right = _clone(Root->_right); 87 root->_left = left; 88 root->_right = right; 89 return root; 90 } 91 else 92 return nullptr; 93 } 94 95 template<typename T>template<typename F> 96 void BinTree<T>::_travPre(TreeNode<T>* root, F&& f) 97 { 98 Stack<TreeNode<T>*>s; 99 TreeNode<T>* cur = root; 100 while (1) 101 { 102 while (cur) 103 { 104 f(cur->_val); 105 s.push(cur->_right); 106 cur = cur->_left; 107 } 108 if (s.empty())break; 109 cur = s.top(); 110 s.pop(); 111 } 112 } 113 114 115 template<typename T>template<typename F> 116 void BinTree<T>::_travIn(TreeNode<T>* root, F&& f) 117 { 118 Stack<TreeNode<T>*>s; 119 TreeNode<T>* cur = root; 120 while (1) 121 { 122 if (cur) 123 { 124 s.push(cur); 125 cur = cur->_left; 126 } 127 else if (!s.empty()) 128 { 129 cur = s.top(); 130 s.pop(); 131 f(cur->_val); 132 cur = cur->_right; 133 } 134 else break; 135 } 136 } 137 138 template<typename T>template<typename F> 139 void BinTree<T>::_travPost(TreeNode<T>* root, F&& f) 140 { 141 if (!root)return; 142 Stack<TreeNode<T>*>s; 143 TreeNode<T>* cur = root; 144 s.push(cur); 145 bool backTrack = false; 146 while (!s.empty()) 147 { 148 cur = s.top(); 149 if (!backTrack && cur->_right) 150 { 151 s.push(cur->_right); 152 backTrack = false; 153 } 154 if (!backTrack && cur->_left) 155 { 156 s.push(cur->_left); 157 backTrack = false; 158 } 159 if (backTrack || (!cur->_left && !cur->_right)) 160 { 161 f(cur->_val); 162 s.pop(); 163 backTrack = true; 164 } 165 if (!s.empty() && (s.top()->_left != cur && s.top()->_right != cur)) 166 backTrack = false; 167 } 168 } 169 170 template<typename T>template<typename F> 171 void BinTree<T>::_travLevel(TreeNode<T>* root, F&& f) 172 { 173 Queue<TreeNode<T>*>q; 174 if (root)q.push(root); 175 while (!q.empty()) 176 { 177 TreeNode<T>* cur = q.front(); 178 f(cur->_val); 179 if (cur->_left)q.push(cur->_left); 180 if (cur->_right)q.push(cur->_right); 181 q.pop(); 182 } 183 } 184 185 186 template<typename T> 187 int BinTree<T>::_remove(TreeNode<T>* rm) 188 { 189 if (!rm)return 0; 190 int n = 1 + _remove(rm->_left) + _remove(rm->_right); 191 delete rm; 192 return n; 193 } 194 195 template<typename T> 196 int BinTree<T>::_updateHeight(TreeNode<T>* p) 197 { 198 return p->_height = max(Height(p->_left), Height(p->_right)) + 1; 199 } 200 201 template<typename T> 202 void BinTree<T>::_updateHeightAbove(TreeNode<T>* p) 203 { 204 while (p) 205 { 206 _updateHeight(p); 207 p = p->_parent; 208 } 209 } 210 211 template<typename T> 212 bool BinTree<T>::_isRoot(TreeNode<T>* p) 213 { 214 return !p->_parent; 215 } 216 217 template<typename T> 218 bool BinTree<T>::_isLeaf(TreeNode<T>* p) 219 { 220 return !p->_left && !p->_right; 221 } 222 223 224 template<typename T> 225 void BinTree<T>::_setRoot(const T& e) 226 { 227 if (_root)remove(_root); 228 _root = new TreeNode<T>(e); 229 _size = 1; 230 } 231 232 template<typename T> 233 BinTree<T>::BinTree(const BinTree<T>& Tree) :_size(0), _root(nullptr) 234 { 235 _root = _clone(Tree._root); 236 _size = Tree._size; 237 } 238 239 template<typename T> 240 BinTree<T>& BinTree<T>::operator=(const BinTree<T>& Tree) 241 { 242 if (this == &Tree)return *this; 243 if (_root)remove(_root); 244 _root = _clone(Tree._root); 245 _size = Tree._size; 246 return *this; 247 } 248 249 template<typename T> 250 BinTree<T>::~BinTree() 251 { 252 remove(_root); 253 } 254 255 template<typename T> 256 void BinTree<T>::remove(TreeNode<T>* rm) 257 { 258 if (!rm)return; 259 if (rm->_parent && rm->_parent->_left == rm)rm->_parent->_left = nullptr; 260 else if (rm->_parent && rm->_parent->_right == rm)rm->_parent->_right = nullptr; 261 this->_updateHeightAbove(rm->_parent); 262 int n = _remove(rm); 263 _size -= n; 264 }
子树删除
1 template<typename T> 2 int BinTree<T>::_remove(TreeNode<T>* rm) 3 { 4 if (!rm)return 0; //递归基 5 //递归删除左右子树 6 int n = 1 + _remove(rm->_left) + _remove(rm->_right); 7 //删除当前节点 8 delete rm; 9 //返回删除的节点数 10 return n; 11 } 12 13 template<typename T> 14 void BinTree<T>::remove(TreeNode<T>* rm) 15 { //删除二叉树节点rm 16 if (!rm)return; 17 //切断来自其父亲节点的引用 18 if (rm->_parent && rm->_parent->_left == rm)rm->_parent->_left = nullptr; 19 else if (rm->_parent && rm->_parent->_right == rm)rm->_parent->_right = nullptr; 20 //因为删除节点,其父亲节点高度可能会变化 21 //更新父亲节点的高度 22 this->_updateHeightAbove(rm->_parent); 23 //记录删除的节点总数 24 int n = _remove(rm); 25 //更新树的规模 26 _size -= n; 27 }
高度更新
二叉树任一节点的高度等于其孩子节点的最大高度加一。因此每当一个节点的孩子或后代有所增减,其高度都有必要及时更新。此处采用另一种策略,即一旦有节点离开或加入二叉树,就更新其所有祖先的高度,这与前述方法等效。
1 template<typename T> 2 int BinTree<T>::_updateHeight(TreeNode<T>* p) 3 { 4 return p->_height = max(Height(p->_left), Height(p->_right)) + 1; 5 } 6 7 template<typename T> 8 void BinTree<T>::_updateHeightAbove(TreeNode<T>* p) 9 { 10 while (p) 11 { 12 _updateHeight(p); 13 p = p->_parent; 14 } 15 }
先序遍历
先访问节点本身,再分别访问其左右子树。
1 template<typename T>template<typename F> 2 void BinTree<T>::_travPre(TreeNode<T>* root, F&& f) 3 { //先序遍历递归版本 4 if (!root)return; 5 f(root->_val); 6 _travPre(root->_left, f); 7 _travPre(root->_right, f); 8 } 9 10 template<typename T>template<typename F> 11 void BinTree<T>::_travPre(TreeNode<T>* root, F&& f) 12 { //先序遍历迭代版本 13 Stack<TreeNode<T>*>s; //辅助栈 14 TreeNode<T>* cur = root; 15 while (1) 16 { 17 while (cur) 18 { 19 f(cur->_val); //访问当前节点 20 s.push(cur->_right); //右节点入栈 21 cur = cur->_left; //沿左侧分支深入一层 22 } 23 if (s.empty())break; //栈空就退出循环 24 cur = s.top(); //下一轮循环 25 s.pop(); 26 } 27 }
以先序遍历为例,以上为递归和迭代版本的实现,中序遍历和后序遍历的递归算法与之类似。递归版的实现更加简明,然而其空间、时间复杂度相对于迭代版都更大。
中序遍历
对于后面的二叉搜索树,中序遍历至关重要,相关算法的一项基本操作,就是定位任一节点在中序遍历序列中的直接后继。
1 template<typename T> 2 TreeNode<T>* TreeNode<T>::succ() 3 { 4 TreeNode<T>* s = this; 5 //若有右子树,则直接后继必在右子树中 6 if (_right) 7 { 8 s = _right; //在右子树中 9 //最靠左(最小)的节点 10以上是关于数据结构——二叉树的主要内容,如果未能解决你的问题,请参考以下文章