树是数据结构中非常重要的一部分内容,尤其是二叉树。
什么是二叉树?
树是由n(n>=1)个有限节点组成一个具有层次关系的集合。把它叫做“树”是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。
二叉树则是每个结点最多有两个子树的树,二叉树的度最多为2,。左侧子树的称为左子树,右侧子树的称为右子树。
二叉树中有着一些非常重要的推论:
1. 度为2的结点比度为0的结点少1个;
2. 具有n个结点的完全二叉树的深度k 为log2(n+1)个结点;
3. 若规定根结点的层数为1,则非空二叉树第i层上最多有2^(i-1)个结点;
4. 若规定只有根结点的二叉树深度为1,则深度为k的二叉树最多有2^k-1个结点;
5. 若对于具有n个结点的完全二叉树,从上至下,从左至右排序依次从0开始,那么对于序号i:
(1)若i>0序号为i的结点的双亲结点序号为(i-1)/2;
(2)若2*i+1<n,则序号为i的结点左孩子结点序号为2*i+1;若2*i+1>=n,则无左孩子;
(3)若2*i+1<n,则序号为i的结点右孩子结点序号为2*i+2;若2*i+1>=n,则无右孩子;
二叉树的结构
struct BinTreeNode { BinTreeNode(const T& data) : _pLeft(NULL),_pRight(NULL),_data(data) {} BinTreeNode<T>* _pLeft; BinTreeNode<T>* _pRight; T _data; };二叉树的遍历:二叉树由左子树+根+右子树构成,二叉树的遍历可以分为四种:前序,中序,后序层序遍历四种。
前序遍历:根+左子树+右子树,即:先访问根,在访问左子树,在访问右子树;
中序遍历:左子树+根+右子树,即:先访问左子树,在访问根,在访问右子树;
后序遍历:左子树+右子树+根,即:先访问左子树,在访问右子树,在访问根;
层序遍历:按层的顺序,一层一层的遍历。
void _PreOrder(pNode pRoot)//前序 { if (pRoot) { cout << pRoot->_data << " "; _PreOrder(pRoot->_pLeft); _PreOrder(pRoot->_pRight); } } void _InOrder(pNode pRoot)//中序 { if (pRoot) { _InOrder(pRoot->_pLeft); cout << pRoot->_data << " "; _InOrder(pRoot->_pRight); } } void _PostOrder(pNode pRoot)//后序 { if (pRoot) { _PostOrder(pRoot->_pLeft); _PostOrder(pRoot->_pRight); cout << pRoot->_data << " "; } } void _LevelOrder(pNode pRoot)//层序:层序的实现较为复杂,需要借助到队列,创建队列,将根结点入队,取队头元素 { //访问队头元素并出队列,如果左子树不为空,则保存左子树结点,保存右子树结点。 if (NULL == pRoot) return; queue<pNode> q; q.push(pRoot); while (!q.empty()) { pNode pCur = q.front(); cout << pCur->_data << " "; q.pop(); if (pCur->_pLeft) q.push(pCur->_pLeft); if (pCur->_pRight) q.push(pCur->_pRight); } }二叉树的创建:我们可以按照前序遍历的思想,先创建根结点,在创建左子树,在创建右子树;在创建的过程中,为了方便创建,我们将‘#’作为无效的标志,当结点无左孩子或右孩子时我们添加一个‘#’。
void _CreateBinTree(pNode& pRoot,const T* arr,size_t size,size_t& index,const T& invalid) { ////index必须传引用,否则函数调用完,index变回原来的值 if (index < size && '#' != arr[index])//先后次序,首先不能越界,再判断是否为有效值,越界时二叉树已创立完成,不需要在判断是否为有效值了 { pRoot = new Node(arr[index]); _CreateBinTree(pRoot->_pLeft,arr,size,++index,invalid); _CreateBinTree(pRoot->_pRight,invalid); } }
下面我们还要计算二叉树的结点的个数,叶子结点的个数,第K层结点的个数,树的高度,查找值为data的结点,查找双亲结点,查找孩子节点,求一个二叉树的镜像以及判断一个树是不是完全二叉树。
template <class T> class BinTree { typedef BinTreeNode<T>* pNode; typedef BinTreeNode<T> Node; public: //////////////////////////////////////构造函数 拷贝构造 析构函数/////////////////////////////////////// BinTree() : _pRoot(NULL) {} BinTree(const T*arr,const T& invalid)//构造函数 { size_t index = 0; _CreateBinTree(_pRoot,index,invalid);//创建二叉树 } BinTree(const BinTree<T>& bt) { _pRoot = _CopyBinTree(bt._pRoot); } BinTree& operator=(const BinTree& bt)//赋值运算符重载 { if (this != &bt) { _DesrtoryBinTree(_pRoot);//当前树可能不为空,因此先要销毁原树 _pRoot = _CopyBinTree(bt._pRoot); } return *this; } ~BinTree()//析构函数 { _DesrtoryBinTree(_pRoot); } ////////////////////////////////////////////////遍历函数///////////////////////////////////////////////// void PreOrder()//前序 { _PreOrder(_pRoot); cout << endl; } void PreOrder_N1() { if (NULL == _pRoot) return; stack<pNode> s; s.push(_pRoot); while (!s.empty()) { pNode pTop = s.top(); cout << pTop->_data << " "; s.pop(); if (pTop->_pRight) s.push(pTop->_pRight); if (pTop->_pLeft) s.push(pTop->_pLeft); } cout << endl; } void PreOrder_N2() { if (NULL == _pRoot) return; stack<pNode> s; s.push(_pRoot); while (!s.empty()) { pNode pCur = s.top(); s.pop(); while (pCur) { cout << pCur->_data << " "; if (pCur->_pRight) s.push(pCur->_pRight); pCur = pCur->_pLeft; } } cout << endl; } void InOrder()//中序 { _InOrder(_pRoot); cout << endl; } void InOrder_N() { if (NULL == _pRoot) return; stack<pNode> s; pNode pCur = _pRoot; while (pCur || !s.empty()) { while (pCur)//找到最左侧的结点 { s.push(pCur); pCur = pCur->_pLeft; } pCur = s.top(); cout << pCur->_data << " "; s.pop(); pCur = pCur->_pRight;//将右子树也是一个树 } cout << endl; } void PostOrder()//后序 { _PostOrder(_pRoot); cout << endl; } void PostOrder_N() { if (NULL == _pRoot) return; pNode pCur = _pRoot; pNode prev = NULL; stack<pNode> s; while (pCur || !s.empty()) { while (pCur) { s.push(pCur); pCur = pCur->_pLeft; } pNode pTop = s.top(); if (pTop->_pRight == NULL || prev == pTop->_pRight) { cout << pTop->_data << " "; prev = pTop; s.pop(); } else pCur = pTop->_pRight; } cout << endl; } void LevelOrder()//层序 { _LevelOrder(_pRoot); cout << endl; } //////////////////////////////////////////结点个数,高度等/////////////////////////////////////////// size_t Size()//结点个数 { return _Size(_pRoot); } size_t GetLeafCount()//叶子结点个数 { return _GetLeafCount(_pRoot); } size_t GetKLevelCount(size_t K)//获取第K层结点的个数 { return _GetLevelCount(_pRoot,K); } size_t Height()//树的高度 { return _Height(_pRoot); } pNode Find(const T& data)//查找值为data的结点 { return _Find(_pRoot,data); } pNode Parent(pNode PNode)//找双亲结点 { return _Parent(_pRoot,PNode); } pNode LeftChild(pNode PNode)//找左孩子 { return (NULL == PNode) ? NULL : PNode->_pLeft; } pNode RightChild(pNode PNode)//找右孩子 { return (NULL == PNode) ? NULL : PNode->_pRight; } void Mirror()//递归镜像 { _Mirror(_pRoot); } void Mirror_N()//非递归镜像 { if (NULL == _pRoot) return; queue<pNode> q; q.push(_pRoot); while (!q.empty()) { pNode pCur = q.front(); swap(pCur->_pLeft,pCur->_pRight); q.pop(); if (pCur->_pLeft) q.push(pCur->_pLeft); if (pCur->_pRight) q.push(pCur->_pRight); } } bool IsCompleteBinTree()//判断是否为完全二叉树 { if (NULL == _pRoot) return true; queue<pNode> q; q.push(_pRoot); bool flag = false; pNode pCur = NULL; while (!q.empty()) { pCur = q.front();//分为四种,当左右都不存在,保存下来,当左存在右不存在,将flag改为true,再判断 //当左不存在右存在,一定不是完全二叉树,当左右都不存在,改为true,继续判断 if (flag && (pCur->_pLeft || pCur->_pRight)) { return false;//当pCur为不饱和结点的时候,flag为true,此时pCur若左孩子或右孩子都不是完全二叉树 } else { if (pCur->_pLeft && pCur->_pRight) { q.push(pCur->_pLeft); q.push(pCur->_pRight); } else if (pCur->_pLeft)//不饱和结点 { q.push(pCur->_pLeft); flag = true; } else if (pCur->_pRight) return false; else//不饱和结点 flag = true; } q.pop(); } return true; } private: void _CreateBinTree(pNode& pRoot,const T& invalid)//创建二叉树 { //index必须传引用,否则函数调用完,index变回原来的值 不能引用常量 if (index < size && invalid != arr[index])//先后次序,首先不能越界 { pRoot = new Node(arr[index]);//创建根结点 _CreateBinTree(pRoot->_pLeft,invalid);//创建左子树 _CreateBinTree(pRoot->_pRight,invalid);//创建右子树 } } pNode _CopyBinTree(pNode pRoot)//拷贝二叉树 { pNode pNewRoot = NULL; if (pRoot) { pNewRoot = new Node(pRoot->_data);//拷贝根节点 if (pRoot->_pLeft)//左子树不存在就不用拷了 pNewRoot->_pLeft = _CopyBinTree(pRoot->_pLeft);//拷贝左子树 if (pRoot->_pRight) pNewRoot->_pRight = _CopyBinTree(pRoot->_pRight);//拷贝右子树 } return pNewRoot; } void _DesrtoryBinTree(pNode pRoot)//销毁二叉树 {//采用后序遍历的思想,否则,先销毁根节点,左右子树将找不到了 if (pRoot) { _DesrtoryBinTree(pRoot->_pLeft);//销毁左子树 _DesrtoryBinTree(pRoot->_pRight);//销毁右子树 delete pRoot;//销毁根节点 pRoot = NULL; } } void _PreOrder(pNode pRoot)//前序递归 { if (pRoot) { cout << pRoot->_data << " "; _PreOrder(pRoot->_pLeft); _PreOrder(pRoot->_pRight); } } void _InOrder(pNode pRoot)//中序递归 { if (pRoot) { _InOrder(pRoot->_pLeft); cout << pRoot->_data << " "; _InOrder(pRoot->_pRight); } } void _PostOrder(pNode pRoot)//后序递归 { if (pRoot) { _PostOrder(pRoot->_pLeft); _PostOrder(pRoot->_pRight); cout << pRoot->_data << " "; } } void _LevelOrder(pNode pRoot)//中序 { if (NULL == pRoot) return; queue<pNode> q; q.push(pRoot); while (!q.empty()) { pNode pCur = q.front(); cout << pCur->_data << " "; q.pop(); if (pCur->_pLeft) q.push(pCur->_pLeft); if (pCur->_pRight) q.push(pCur->_pRight); } } size_t _Size(pNode pRoot)//结点个数 {//左子树结点个数+右子树结点个数+1 if (pRoot == NULL) return 0; return _Size(pRoot->_pLeft) + _Size(pRoot->_pRight) + 1; } size_t _GetLeafCount(pNode pRoot)//叶子结点个数 {//左子树叶子结点个数+右子树叶子结点个数 if (pRoot == NULL) return 0; if (pRoot->_pLeft == NULL && pRoot->_pRight == NULL) return 1; return _GetLeafCount(pRoot->_pLeft) + _GetLeafCount(pRoot->_pRight); } size_t _GetLevelCount(pNode pRoot,size_t K)//第K层 { if (K == 0 || pRoot == NULL)//size_t是大于等于0的,不用考虑小于0 return 0; if (K == 1) return 1; return _GetLevelCount(pRoot->_pLeft,K - 1) + _GetLevelCount(pRoot->_pRight,K - 1); } size_t _Height(pNode pRoot)//高度 { if (pRoot == NULL) return 0; if (pRoot->_pLeft == NULL && pRoot->_pRight == NULL) return 1; size_t leftNode = _Height(pRoot->_pLeft); return _Height(pRoot->_pRight) > leftNode ? _Height(pRoot->_pRight) + 1 : leftNode + 1; } pNode _Find(pNode pRoot,const T& data)//查找值为data的结点 { if (NULL == pRoot) return NULL; if (pRoot->_data == data) return pRoot; pNode tmp = _Find(pRoot->_pLeft,data);//先在根找,根没找到,再去左子树找,找到直接返回,没找到再去右子树 return (NULL == tmp) ? _Find(pRoot->_pRight,data) : tmp; } pNode _Parent(pNode pRoot,pNode PNode)//找双亲结点 { if (NULL == pRoot || pRoot == PNode || PNode == NULL) return NULL; if (pRoot->_pLeft == PNode || pRoot->_pRight == PNode) return pRoot; pNode tmp = _Parent(pRoot->_pLeft,PNode); return (NULL == tmp) ? _Parent(pRoot->_pRight,PNode) : tmp; } void _Mirror(pNode pRoot)//镜像 { if (NULL == pRoot) return; if (pRoot->_pLeft || pRoot->_pRight) swap(pRoot->_pLeft,pRoot->_pRight); _Mirror(pRoot->_pLeft); _Mirror(pRoot->_pRight); } private: pNode _pRoot; };下面是我的测试函数:
void Test() { char* pStr = "abd###ce##f"; BinTree<char> bt(pStr,strlen(pStr),'#'); if (bt.IsCompleteBinTree()) cout << "是完全二叉树" << endl; else cout << "不是完全二叉树" << endl; bt.PreOrder(); bt.Mirror(); bt.Mirror_N(); bt.PreOrder(); bt.PostOrder_N(); bt.PreOrder_N1(); bt.PreOrder_N2(); bt.InOrder_N(); bt.PreOrder(); bt.LevelOrder(); bt.InOrder(); bt.PostOrder(); cout << bt.Size() << endl; cout << bt.GetLeafCount() << endl; cout << bt.Height() << endl; BinTreeNode<char>* pcur = bt.Parent(bt.Find('b')); cout << pcur->_data << endl; cout << bt.LeftChild(bt.Find('a'))->_data << endl; cout << bt.LeftChild(bt.Find('b'))->_data << endl; if (bt.RightChild(bt.Find('b'))) cout << bt.RightChild(bt.Find('b'))->_data << endl; cout << bt.LeftChild(bt.Find('c'))->_data << endl; cout << bt.RightChild(bt.Find('c'))->_data << endl; cout << bt.GetKLevelCount(1) << endl; cout << bt.GetKLevelCount(2) << endl; cout << bt.GetKLevelCount(3) << endl; BinTree<char> bt1(bt); bt1.PreOrder(); BinTree<char> bt2; bt2 = bt; bt2.PreOrder(); }这些函数基本都用到了递归+前序遍历或后序遍历的思想,有的借助到了栈或者队列,其实现方法并不复杂。 原文链接:https://www.f2er.com/datastructure/382247.html