数据结构系列文章 |
---|
数据结构图文解析之:数组、单链表、双链表介绍及C++模板实现 |
数据结构图文解析之:栈的简介及C++模板实现 |
数据结构图文解析之:队列详解与C++模板实现 |
数据结构图文解析之:树的简介及二叉排序树C++模板实现. |
数据结构图文解析之:AVL树详解及C++模板实现 |
数据结构图文解析之:二叉堆详解及C++模板实现 |
数据结构图文解析之:哈夫曼树与哈夫曼编码详解及C++模板实现 |
树是一种数据结构,它是n(n>=0)个节点的有限集。n=0时称为空树。n>0时,有限集的元素构成一个具备层次感的数据结构。
html
区别于线性表一对一的元素关系,树中的节点是一对多的关系。树具备如下特色:node
树有许多相关的术语与概念,在学习树的结构以前,咱们要熟悉这些概念。git
二叉树或者为空集,或者由一个根节点和两棵互不相交的、分别称为左子树和右子树的二叉树组成。从定义能够看出一棵二叉树:github
根据定义,一棵二叉树有5中基本形态:算法
全部节点都只有左子树的二叉树叫作左斜树,全部节点都只有右子树的二叉树叫作右斜树。左斜树和右子树统称为斜树。
斜树已经退化成线性结构,二叉树在查找上表现出来优异性能在斜树得不到体现。
数组
注意:为了只关注重点,咱们全部的节点都采用统一浅绿色着色,如有特殊节点将在图中备注数据结构
满二叉树要知足两个条件:函数
在一样深度的二叉树中,满二叉树的节点数目是最多的,叶子数也是最多的。
post
在一棵二叉树中,只有最下两层的度能够小于2,而且最下一层的叶子节点集中出如今靠左的若干位置上。
或者这样定义:对一棵具备n个节点的二叉树按层序从左到右编序,二叉树树某个节点的编序与一样位置的满二叉树节点的编序相同若是全部节点都知足这个条件,则二叉树为彻底二叉树。
从定义能够看出: 满二叉树必定是彻底二叉树;彻底二叉树不必定是满二叉树。性能
二叉排序树也称为二叉搜索树或二叉排序树。二叉排序树的节点包含键值key。二叉排序树或者是一棵空树,不然要求:
根据定义,二叉查找树中没有重复key的节点。
在实际的应用中,二叉排序树的应用比较多,咱们后面要讲的AVL树自己也是一种二叉排序树。
证实:利用数学概括法进行证实
证实:二叉树节点数最多时,每层的节点树都必须最多。
根据性质一,深度为k的二叉树的节点数最多为: 2^0 + 2^1 +....+2^(k-1) = 2 ^ k -1
证实:二叉树节点度数最大为2,则 : n = n0 + n1 + n2 (等式一)
从孩子个数角度出发: 度为0的节点没有孩子, 度为1的节点没有1个孩子,度为2的节点有2个孩子,孩子总数为 n00 + n11 +n2 2 = n1+2n2;树的全部节点中,只有根不是任何节点的孩 子,所以有 n -1 = n1 + 2* n2 ,即 n = n1 + 2* n2 + 1. (等式二)
由等式一等式而能够推出 n0 = n2 +1
证实:高度为h的二叉树最多有2{h}–1个结点。反之,对于包含n个节点的二叉树的高度至少为log2(n+1)。
二叉查找树的定义咱们已经知道。要维护二叉查找树的特性,比较复杂的是删除节点操做,咱们将进行重点的解析。不过咱们先来看看二叉查找树的节点结构定义与类定义。
//二叉查找树的节点结构 template <typename T> struct BSNode { BSNode(T t) : value(t), lchild(nullptr), rchild(nullptr){} BSNode() = default; T value; BSNode<T>* lchild; BSNode<T>* rchild; BSNode<T>* parent; };
//二叉查找树类 template <typename T> class BSTree { public: BSTree(); ~BSTree(); void preOrder(); //前序遍历二叉树 void inOrder(); //中序遍历二叉树 void postOrder(); //后序遍历二叉树 void layerOrder(); //层次遍历二叉树 BSNode<T>* search_recursion(T key); //递归地进行查找 BSNode<T>* search_Iterator(T key); //迭代地进行查找 T search_minimun(); //查找最小元素 T search_maximum(); //查找最大元素 BSNode<T>* successor (BSNode<T>* x); //查找指定节点的后继节点 BSNode<T>* predecessor(BSNode<T>* x); //查找指定节点的前驱节点 void insert(T key); //插入指定值节点 void remove(T key); //删除指定值节点 void destory(); //销毁二叉树 void print(); //打印二叉树 private: BSNode<T>* root; //根节点 private: BSNode<T>* search(BSNode<T>* & p, T key); void remove(BSNode<T>* p, T key); void preOrder(BSNode<T>* p); void inOrder(BSNode<T>* p); void postOrder(BSNode<T>* p); T search_minimun(BSNode<T>* p); T search_maximum(BSNode<T>* p); void destory(BSNode<T>* &p); };
这里咱们定义了二叉排序树的类型BSTree。它包含了:
提供的其余接口都有相应的备注说明。
假设咱们要为数组 a[] = {10 , 5 , 15 , 6 , 4 , 16 }构建一个二叉排序树,咱们按顺序逐个插入元素。
插入过程是这样的:
从这个过程咱们能够总结出插入新元素的步骤:
该过程的实现代码:
/*插入函数*/ template <typename T> void BSTree<T>::insert(T key) { BSNode<T>* pparent = nullptr; BSNode<T>* pnode = root; while (pnode != nullptr) //寻找合适的插入位置 { pparent = pnode; if (key > pnode->value) pnode = pnode->rchild; else if (key < pnode->value) pnode = pnode->lchild; else break; } pnode = new BSNode<T>(key); //以元素的值构建新节点 if (pparent == nullptr) //若是是空树 { root = pnode; //则新节点为根 } else { if (key > pparent->value) { pparent->rchild = pnode;//不然新节点为其父节点的左孩 } else pparent->lchild = pnode; //或右孩 } pnode->parent = pparent; //指明新节点的父节点 };
将构建出来的新节点插入二叉排序树时,须要修改连接指针的指向。
遍历平衡二叉树,就是以某种方式逐个“访问”二叉树的每个节点。“访问”是指对节点的进行某种操做,例如输出节点的值。
平衡二叉树是有序树,严格区分左子树与右子树,若是规定左子树先于右子树的次序,咱们有三种方式遍历二叉树:
咱们以如图的两棵二叉排序树进行遍历的算法演示。
若二叉树为空,则空操做返回,不然先访问根节点,而后前序遍历左子树,再前序遍历右子树。(简记为:VLR)
/*前序遍历算法*/ template <typename T> void BSTree<T>::preOrder() { preOrder(root); }; template <typename T> void BSTree<T>::preOrder(BSNode<T> *p) { if (p != nullptr) { cout << p->value << endl; preOrder(p->lchild); preOrder(p->rchild); } };
前序遍历树a:10 5 4 3 6 15 16
前序遍历树b:5 3 2 4 8 7 9
若二叉树为空,则空操做返回,不然从根节点开始,中序遍历根节点的左子树,而后访问根节点,最后中序遍历右子树。(简记为:LVR)
/*中序遍历算法*/ template <typename T> void BSTree<T>::inOrder() { inOrder(root); }; template<typename T> void BSTree<T>::inOrder(BSNode<T>* p) { if (p != nullptr) { inOrder(p->lchild); cout << p->value << endl; inOrder(p->rchild); } };
前序遍历树a:3 4 5 6 10 15 16
前序遍历树b:2 3 4 5 7 8 9
二叉排序树的中序遍历恰好输出一个非递减的有序序列。
若树为空,则返回空操做,不然从左到右先叶子后节点的方式遍历访问左右子树,左右子树都访问结束,才访问根节点。(简称LRV)
/*后序遍历算法*/ template <typename T> void BSTree<T>::postOrder() { postOrder(root); }; template <typename T> void BSTree<T>::postOrder(BSNode<T>* p) { if (p != nullptr) { postOrder(p->lchild); postOrder(p->rchild); cout << p->value<<endl; } };
后序遍历树a:3 4 6 5 16 15 10
后序遍历树b:2 4 3 7 9 8 5
对于一棵二叉排序树,中序遍历时恰好能够输出一个非递减的序列。例如前序遍历图九树a:3 4 5 6 10 15 16,则可称:
一个节点的前驱节点有3种状况:
/*寻找其前驱节点*/ template <typename T> BSNode<T>* BSTree<T>::predecessor(BSNode<T>* pnode) { if (pnode->lchild != nullptr) { pnode = pnode->lchild; while (pnode->rchild != nullptr) { pnode = pnode->rchild; } return pnode; } BSNode<T>* pparent = pnode->parent; while (pparent != nullptr && pparent->lchild == pnode)//若是进入循环,则是第三种状况;不然为第二种状况 { pnode = pparent; pparent = pparent->parent; } return pparent; };
一样的,一个节点的后继节点也有三种状况:
/*寻找其后继节点*/ template <typename T> BSNode<T>* BSTree<T>::successor(BSNode<T>* pnode) { if (pnode->rchild != nullptr) { pnode = pnode->rchild; while (pnode->lchild != nullptr) { pnode = pnode->lchild; } return pnode; } BSNode<T>* pparent = pnode->parent; while (pparent!=nullptr&& pparent->rchild == pnode) { pnode = pparent; pparent = pparent->parent; } return pparent; };
删除二叉排序树的某个节点有三种状况:
对于第一种状况,咱们的处理方式是将前驱节点的值保存在当前结点,继而删除前驱节点。
对于第二种状况,咱们直接用子树替换被删节点。
对于第三种状况,咱们能够直接删除节点。
删除节点的代码:
/*删除指定节点*/ template <typename T> void BSTree<T>::remove(T key) { remove(root, key); }; /*删除指定节点*/ /*内部使用函数*/ template <typename T> void BSTree<T>::remove(BSNode<T>* pnode, T key) { if (pnode != nullptr) { if (pnode->value == key) { BSNode<T>* pdel=nullptr; if (pnode->lchild == nullptr || pnode->rchild == nullptr) pdel = pnode; //状况2、三:被删节点只有左子树或右子树,或没有孩子 else pdel = predecessor(pnode); //状况一:被删节点同时有左右子树,则删除该节点的前驱 //此时,被删节点只有一个孩子(或没有孩子).保存该孩子指针 BSNode<T>* pchild=nullptr; if (pdel->lchild != nullptr) pchild = pdel->lchild; else pchild = pdel->rchild; //让孩子指向被删除节点的父节点 if (pchild != nullptr) pchild->parent = pdel->parent; //若是要删除的节点是头节点,注意更改root的值 if (pdel->parent == nullptr) root = pchild; //若是要删除的节点不是头节点,要注意更改它的双亲节点指向新的孩子节点 else if (pdel->parent->lchild==pdel) { pdel->parent->lchild = pchild; } else { pdel->parent->rchild = pchild; } if (pnode->value != pdel->value) pnode->value = pdel->value; delete pdel; } //进行递归删除 else if (key > pnode->value) { remove(pnode->rchild, key); } else remove(pnode->lchild, key); } };
咱们能够递归或非递归地进行元素的查找。元素的查找过程与元素的插入过程一致,也是在不断地与当前结点进行比较,若值比当前节点的值大,则在右子树进行查找,若值比当前节点的值小,则在左子树进行查找,能够看到这是一个很适合递归操做的过程。而因为二叉排序树这种左小右大的节点特征,也很容易进行非递归查找。
/*查找指定元素的节点(非递归)*/ template <typename T> BSNode<T>* BSTree<T>::search_Iterator(T key) { BSNode<T> * pnode = root; while (pnode != nullptr) { if (key == pnode->value) //找到 return pnode; if (key > pnode->value) //关键字比节点值大,在节点右子树查找 pnode = pnode->rchild; else pnode = pnode->lchild; //关键字比节点值小,在节点左子树查找 } return nullptr; }; /*查找指定元素的节点(递归)*/ template <typename T> BSNode<T>* BSTree<T>::search_recursion(T key) { return search(root, key); }; /*private:search()*/ /*递归查找的类内部实现*/ template <typename T> BSNode<T>* BSTree<T>::search(BSNode<T>* & pnode, T key) { if (pnode == nullptr) return nullptr; if (pnode->value == key) return pnode; //cout << "-->" << pnode->value << endl; //能够输出查找路径 if (key > pnode->value) return search(pnode->rchild, key); return search(pnode->lchild, key); };
二叉排序树的最小值位于其最左节点上;最大值位于其最右节点上:
/*寻找最小元素*/ template <typename T> T BSTree<T>::search_minimun() { return search_minimun(root); }; template <typename T> T BSTree<T>::search_minimun(BSNode<T>* p) { if (p->lchild != nullptr) return search_minimun(p->lchild); return p->value; }; /*寻找最大元素*/ template <typename T> T BSTree<T>::search_maximum() { return search_maximum(root); }; template <typename T> T BSTree<T>::search_maximum(BSNode<T>*p) { if (p->rchild != nullptr) return search_maximum(p->rchild); return p->value; };
使用后序遍历递归销毁二叉树
/*销毁二叉树*/ template<typename T> void BSTree<T>::destory() { destory(root); }; template <typename T> void BSTree<T>::destory(BSNode<T>* &p) { if (p != nullptr) { if (p->lchild != nullptr) destory(p->lchild); if (p->rchild != nullptr) destory(p->rchild); delete p; p = nullptr; } };
int main() { BSTree<int> t; t.insert(62); t.insert(58); t.insert(47); t.insert(51); t.insert(35); t.insert(37); t.insert(88); t.insert(73); t.insert(99); t.insert(93); t.insert(95); cout << endl << "中序遍历:" << endl; t.inOrder(); cout << "最大元素:" << t.search_maximum() << endl; cout << "最小元素:" << t.search_minimun() << endl; cout << "删除元素99" << endl; t.remove(99); cout << "最大元素:" << t.search_maximum() << endl; t.destory(); getchar(); return 0; }
运行结果:
中序遍历: 35 37 47 51 58 62 73 88 93 95 99 最大元素:99 最小元素:35 删除元素99 最大元素:95
在github上存放了二叉排序树的vs项目工程。这是二叉排序树的源代码:
https://github.com/huanzheWu/Data-Structure/blob/master/BSTree/BSTree/BSTree.h
原创文章,转载请注明出处:http://www.cnblogs.com/QG-whz/p/5168620.html#_label0