二叉树
Binary Tree Link
二叉树的一些性质
//二叉树的性质 //性质一:二叉树的第 i 层, 最多有2的 i - 1 次方个节点 (i >= 1) //性质二: 高度为 k 的二叉树至多有 (2 的k 次方) - 1 个节点 ( k >= 1) //性质三: 二叉树的总数量等于节点的总度数 + 1 //性质四: 任何一个二叉树,若叶子节点数量为 n0 ,度为 2 的节点数量为 n2, 则叶子节点的数量比有 //两个子树的节点数量多一个,即: n0 = n2 + 1 //若假设度为1的节点数量为n1, 那么二叉树节点总数量: n = n0 + n1 + n2; //节点总度数 = 2 * n2 + n1; //根据性质3,节点的总数量 n = 2 * n2 + n1 + 1 // n0 + n1 + n2 = 2 * n2 + n1 + 1 ==> n0 = n2 + 1 //知道了一个完全二叉树的节点总数 n, 如何求出n0 (叶子节点数量),n1(度为一的数量),n2(度为二的数量) //a) 完全二叉树最多只有一个度为 1 的节点, 即 n1 = 0, 或者 n1 = 1; //b) 根据公式 n = 2 * n2 + n1 + 1,其中 2 * n2+ 1 的结果肯定是奇数(不能被2整除的整数) //c) 如果该完全二叉树节点总数是偶数(能被2整除),那么 n1必定是奇数,也就是 值 1 // 如果完全二叉树节点总数是奇数,那么 n1 必定是偶数也就是值 0 //层序遍历 / 层次遍历 (也称为 广度优先遍历 / 广度优先搜索),一般要借助队列。 //从根节点开始,从上到下,从左到右 //层序遍历的过程 //a)初始化一个队列 //b)把二叉树的根节点入队列 //c)判断队列是否为空,如果不为空,就让队头节点出队(相当于遍历了该节点),同时将这个刚刚出队列的左孩子和右孩子分别入队列 //如果该节点有左右孩子 //d)重复 c) 步骤,一直到队列为空。 //二叉树的存储结构:顺序,链式 //顺序的存储方式:用一段连续的内存单元(数组)一次从上到下从左到右存储二叉树各个节点元素. //存储的是完全二叉树。 根在数组下标为 i = 1。那么左字节点就存储在 2 * i 的位置,右子节点就存储在 2 * i + 1 = 3 的位置。 //链式存储方式:适用于普通的二叉树 //节点结构:一个数据域,两个指针域
节点标记与节点定义
enum ECCHILDSIGN //节点标记,CreateNode()的时候需要使用 { E_Root, //树根 E_ChildLeft, //左孩子 E_ChildRight //右孩子 }; //树中每个节点的定义 template<typename T> //T代表数据元素的类型 struct BinaryTreeNode { T data; //数据域 BinaryTreeNode* LeftChild; //左子节点指针 BinaryTreeNode* RightChild; //右子节点定义 };
二叉树的定义
//二叉树的定义 template<typename T> class BinaryTree { public: BinaryTree(); ~BinaryTree(); public: //创建一个树节点 BinaryTreeNode<T>* CreateNode(BinaryTreeNode<T>* parentnode, ECCHILDSIGN pointSign, const T& e); //利用扩展二叉树的前序遍历序列来创建一颗二叉树 void CreateBTreeAccorPT(char* pstr); private: //利用扩展二叉树的前序遍历序列创建二叉树的递归函数 void CreateBTreeAccorPTRecu(BinaryTreeNode<T>*& tnode, char*& pstr); //参数类型为应用,确保递归函数调用中对参数的改变会影响到调用者 void RleaseNode(BinaryTreeNode<T>* pnode); //释放树节点 public: //遍历操作 void preOrder(); //前序遍历 void inOrder(); //中序遍历 void postOrder(); //后序遍历 private: void preOrder(const BinaryTreeNode<T>* tNode); void inOrder(const BinaryTreeNode<T>* tNode); void postOrder(const BinaryTreeNode<T>* tNode); public: void levelOrder(); //层序遍历二叉树 private: void levelOrder(BinaryTreeNode<T>* tNode); public: int getSize(); //求二叉树节点个数 int getHeight(); //求二叉树高度 BinaryTreeNode<T>* SearchElem(const T& e); //查找某个节点(假设二叉树节点各不相同) BinaryTreeNode<T>* GetParent(BinaryTreeNode<T>* tSonNode); //查找某个节点的父节点 void CopyTree(BinaryTree<T>* targetTree); //树的拷贝 void prePrder_noRecu(); //非递归方式前序遍历二叉树 void inOrder_noRecu(); //非递归方式中序遍历二叉树 void postOrder_noRecu(); //非递归后续遍历二叉树 //如何跟根据前序、中序遍历序列来创建一颗二叉树 //参数pP_t:前序遍历序列, 比如 "ABDCE", PI_T: 中序遍历序列,比如"DBACE" void CreateBTreeAccordPI(char* pP_T, char* pI_T); void CreateBTreeAccordIPO(char* p_IT, char* pPOST_T); //如何根据中序(左根右)、后续遍历(左右根)序列来创建一颗二叉树 private: int getSize(BinaryTreeNode<T>* tNode); int getHeight(BinaryTreeNode<T>* tNode); BinaryTreeNode<T>* SearchElem(BinaryTreeNode<T>* pNode, const T& e); BinaryTreeNode<T>* GetParent(BinaryTreeNode<T>* tParNode, BinaryTreeNode<T>* tSonNode); //tParNode是 tSonNode的爹节点 void CopyTree(BinaryTreeNode<T>* tSource, BinaryTreeNode<T>*& tTarget); //注意第二个参数为引用 void prePrder_noRecu(BinaryTreeNode<T>* tRoot); void inOrder_noRecu(BinaryTreeNode<T>* tRoot); void postOrder_noRecu(BinaryTreeNode<T>* tRoot); void CreateBTreeAccorPT(BinaryTreeNode<T>*& tnode, char* pP_T, char* pI_T, int n); void CreateBTreeAccordIPO(BinaryTreeNode<T>*& tnode, char* pI_T, char* pPOST_T, int n); private: BinaryTreeNode<T>* root; //树根节点 };
构造与析构
//构造函数的实现 template<typename T> BinaryTree<T>::BinaryTree() { root = nullptr; } //析构函数 template<typename T> BinaryTree<T>::~BinaryTree() { RleaseNode(root); }
树节点的释放
//释放二叉树节点 template<typename T> void BinaryTree<T>::RleaseNode(BinaryTreeNode<T>* pnode) { if (pnode != nullptr) { RleaseNode(pnode->LeftChild); RleaseNode(pnode->RightChild); } delete pnode; }
遍历操作
-
前序遍历
template<typename T> void BinaryTree<T>::preOrder() //遍历操作,前序遍历 { preOrder(root); } template<typename T> void BinaryTree<T>::preOrder(const BinaryTreeNode<T>* tNode) { if (tNode != nullptr) //若二叉树非空 { //根左右顺序 cout << (char)tNode->data << " "; //输出节点的数据域值,用char显示字母 preOrder(tNode->LeftChild); preOrder(tNode->RightChild); } }
-
中序遍历
template<typename T> void BinaryTree<T>::inOrder() //中序遍历 { inOrder(root); } template<typename T> void BinaryTree<T>::inOrder(const BinaryTreeNode<T>* tNode) { if (tNode != nullptr) //若二叉树非空 { //左根右顺序 inOrder(tNode->LeftChild); cout << (char)tNode->data << " "; //输出节点的数据域值,用char显示字母 inOrder(tNode->RightChild); } }
-
后续遍历
template<typename T> void BinaryTree<T>::postOrder() //后序遍历 { postOrder(root); } template<typename T> void BinaryTree<T>::postOrder(const BinaryTreeNode<T>* tNode) { if (tNode != nullptr) //若二叉树非空 { //左右根顺序 postOrder(tNode->LeftChild); postOrder(tNode->RightChild); cout << (char)tNode->data << " "; //输出节点的数据域值,用char显示字母 } }
-
层序遍历
template<typename T> void BinaryTree<T>::levelOrder() //层序遍历二叉树 { levelOrder(root); } template<typename T> void BinaryTree<T>::levelOrder(BinaryTreeNode<T>* tNode) { if (tNode != nullptr) //二叉树非空 { BinaryTreeNode<T>* tempnode; LinkQueue<BinaryTreeNode<T>* > lnobj; //注意队列元素类型是节点指针类型 lnobj.EnQueue(tNode); //先把根节点入队 while (!lnobj.IsEmpty()) //判断队列是否为空 { lnobj.DeQueue(tempnode); //出队列 cout << (char)tempnode->data << " "; /* 可以在层序遍历中判断是否是完全二叉树 //左子节点存在,而右子节点不存在,就不是一颗完全二叉树 // if(tempnode->LeftChild == nullptr && tempnode->RightChild != nullptr) // { // //这颗二叉树不是一颗完全二叉树 // cout << "不是完全二叉树" << endl; // } */ if (tempnode->LeftChild != nullptr) lnobj.EnQueue(tempnode->LeftChild); if (tempnode->RightChild != nullptr) lnobj.EnQueue(tempnode->RightChild); }//end while }// end if }
-
前序遍历(非递归)
template<typename T> void BinaryTree<T>::prePrder_noRecu() //非递归方式前序遍历二叉树 { prePrder_noRecu(root); } template<typename T> void BinaryTree<T>::prePrder_noRecu(BinaryTreeNode<T>* tRoot) { if (tRoot == nullptr) { return; } LinkStack<BinaryTreeNode<T>* > slinkobj; slinkobj.Push(tRoot); //根节点入栈 BinaryTreeNode<T>* tmpnode; while (!slinkobj.Empty()) //栈不空 { slinkobj.Pop(tmpnode); //栈顶元素出栈 cout << (char)tmpnode->data << " "; //访问栈顶元素 if (tmpnode->RightChild != nullptr) //注意先判断右树在判断左树 { slinkobj.Push(tmpnode->RightChild); //把右树入栈 } if (tmpnode->LeftChild != nullptr) //注意先判断右树在判断左树 { slinkobj.Push(tmpnode->LeftChild); //把左树入栈 } } }
-
中序遍历(非递归)
template<typename T> void BinaryTree<T>::inOrder_noRecu() //非递归方式中序遍历二叉树 { inOrder_noRecu(root); } template<typename T> void BinaryTree<T>::inOrder_noRecu(BinaryTreeNode<T>* tRoot) { if (tRoot == nullptr) { return; } LinkStack<BinaryTreeNode<T>* > slinkobj; slinkobj.Push(tRoot); //根节点入栈 BinaryTreeNode<T>* tmpnode; while (!slinkobj.Empty()) //栈为空 { while (tRoot->LeftChild != nullptr) { slinkobj.Push(tRoot->LeftChild); //将左子节点入栈 tRoot = tRoot->LeftChild; }//end while slinkobj.Pop(tmpnode); cout << (char)tmpnode->data << " "; //如果被访问的元素的右子节点不为空,则把其右子节点指定为当前节点入栈 if (tmpnode->RightChild != nullptr) { tRoot = tmpnode->RightChild; //将刚刚栈顶出栈的右节点设置为当前节点 slinkobj.Push(tRoot); //右节点入栈 } }//end while }
-
后续遍历(非递归)
template<typename T> void BinaryTree<T>::postOrder_noRecu() //非递归后续遍历二叉树 { postOrder_noRecu(root); } template<typename T> void BinaryTree<T>::postOrder_noRecu(BinaryTreeNode<T>* tRoot) { if (tRoot == nullptr) return; LinkStack<BTNode_extra<T> > slinkobj; BTNode_extra<T> ext_tmpnode; do { while (tRoot != nullptr) //循环2 { ext_tmpnode.point = tRoot; ext_tmpnode.pointSign = E_ChildLeft; //标记先处理该节点的左孩子 slinkobj.Push(ext_tmpnode); tRoot = tRoot->LeftChild; }//end while while (!slinkobj.Empty()) //循环3 { slinkobj.Pop(ext_tmpnode); //出栈 if (ext_tmpnode.pointSign == E_ChildLeft) { ext_tmpnode.pointSign = E_ChildRight; //标记该节点为右孩子 slinkobj.Push(ext_tmpnode); //重新入栈 tRoot = ext_tmpnode.point->RightChild; break; //终止while } else // if(ext_tmpnode.pointSign == E_RightChild) { cout << (char)ext_tmpnode.point->data << " "; } }//end while } while (!slinkobj.Empty()); //循环1 }
求节点个数
template<typename T> int BinaryTree<T>::getSize() //求二叉树节点个数 { return getSize(root); } template<typename T> int BinaryTree<T>::getSize(BinaryTreeNode<T>* tNode) { if (tNode == nullptr) { return 0; } return getSize(tNode->LeftChild) + getSize(tNode->RightChild) + 1; //之所以 + 1,是因为有一个根节点 }
求二叉树高度
template<typename T> int BinaryTree<T>::getHeight() //求二叉树高度 { return getHeight(root); } template<typename T> int BinaryTree<T>::getHeight(BinaryTreeNode<T>* tNode) { if (tNode == nullptr) { return 0; } int lheight = getHeight(tNode->LeftChild); //左子树高度 int rheight = getHeight(tNode->RightChild); //右子树高度 if (lheight > rheight) { return lheight + 1; //之所以加一,是因为还包括根节点高度 } else { return rheight + 1; //之所以加一,是因为还包括根节点 } }
查找某节点
template<typename T> BinaryTreeNode<T>* BinaryTree<T>::SearchElem(const T& e) //查找某个节点(假设二叉树节点各不相同) { return SearchElem(root, e); } template<typename T> BinaryTreeNode<T>* BinaryTree<T>::SearchElem(BinaryTreeNode<T>* pNode, const T& e) { if (pNode == nullptr) { return nullptr; } if (pNode->data == e) //从根开始找 { return pNode; } BinaryTreeNode<T>* p = SearchElem(pNode->LeftChild, e); //查找左孩子 if (p != nullptr) //这里判断不可缺少 return p; return SearchElem(pNode->RightChild, e); //左子树查不到,继续到右子树查找,所以这里直接return }
查找某节点的父节点
template<typename T> BinaryTreeNode<T>* BinaryTree<T>::GetParent(BinaryTreeNode<T>* tSonNode) //查找某个节点的父节点 { return GetParent(root, tSonNode); } template<typename T> BinaryTreeNode<T>* BinaryTree<T>::GetParent(BinaryTreeNode<T>* tParNode, BinaryTreeNode<T>* tSonNode)//tParNode是 tSonNode的爹节点 { if (tParNode == nullptr || tSonNode == nullptr) return nullptr; if (tParNode->LeftChild == tSonNode || tParNode->RightChild == tSonNode) { return tParNode; } BinaryTreeNode<T>* p1 = GetParent(tParNode->LeftChild, tSonNode); //递归,在左边找 if (p1 != nullptr) { return p1; } return GetParent(tParNode->RightChild, tSonNode); //递归,在右边找 }
树的拷贝
template<typename T> void BinaryTree<T>::CopyTree(BinaryTree<T>* targetTree) //树的拷贝 { CopyTree(root, targetTree->root); } template<typename T> void BinaryTree<T>::CopyTree(BinaryTreeNode<T>* tSource, BinaryTreeNode<T>*& tTarget) //注意第二个参数为引用 { if (tSource == nullptr) { tTarget = nullptr; } else { tTarget = new BinaryTreeNode<T>; tTarget->data = tSource->data; CopyTree(tSource->LeftChild, tTarget->LeftChild); //对左子树进行拷贝 CopyTree(tSource->RightChild, tTarget->RightChild); //对右子树进行拷贝 } }
树的创建
-
使用节点标记的创建方式
//创建一个树节点 template<typename T> BinaryTreeNode<T>* BinaryTree<T>::CreateNode(BinaryTreeNode<T>* parentnode, ECCHILDSIGN pointSign, const T& e) { //将新节点创建迟来 BinaryTreeNode<T>* tempnode = new BinaryTreeNode<T>; tempnode->data = e; tempnode->LeftChild = nullptr; tempnode->RightChild = nullptr; //把节点放入正确的位置 if (pointSign == E_Root) { //创建的是根节点 root = tempnode; } if (pointSign == E_ChildLeft) { //创建的是左孩子节点 parentnode->LeftChild = tempnode; } else if (pointSign == E_ChildRight) { //创建的是右孩子节点 parentnode->RightChild = tempnode; } return tempnode; }
-
扩展前序遍历创建方式
//利用扩展二叉树的前序遍历序列来创建一颗二叉树 template<typename T> void BinaryTree<T>::CreateBTreeAccorPT(char* pstr) { CreateBTreeAccorPTRecu(root, pstr); } //利用扩展二叉树的前序遍历序列创建二叉树的递归函数 template<typename T> void BinaryTree<T>::CreateBTreeAccorPTRecu(BinaryTreeNode<T>*& tnode, char*& pstr) { //ABD###C#E## if (*pstr == '#') { tnode = nullptr; } else { //根左右 tnode = new BinaryTreeNode<T>; //创建根节点 tnode->data = *pstr; CreateBTreeAccorPTRecu(tnode->LeftChild, ++pstr); //创建左子树 CreateBTreeAccorPTRecu(tnode->RightChild, ++pstr); //创建右子树 } }
-
前序和中序遍历创建方式
//如何跟根据前序、中序遍历序列来创建一颗二叉树 //参数pP_t:前序遍历序列, 比如 "ABDCE", PI_T: 中序遍历序列,比如"DBACE" template<typename T> void BinaryTree<T>::CreateBTreeAccordPI(char* pP_T, char* pI_T) { CreateBTreeAccorPT(root, pP_T, pI_T, strlen(pP_T)); } //参数1为引用类型,确保递归调用中对参数的改变会影响到使用者。参数n:节点个数 template<typename T> void BinaryTree<T>::CreateBTreeAccorPT(BinaryTreeNode<T>*& tnode, char* pP_T, char* pI_T, int n) { if (n == 0) { tnode = nullptr; } else { //(1)在中序遍历序列中找根,前序遍历序列中根是在最前面 int tmpindex = 0; //下标 while (pP_T[0] != pI_T[tmpindex]) tmpindex++; tnode = new BinaryTreeNode<T>; //创建根节点 tnode->data = pI_T[tmpindex]; //第一次tmpindex == 2 //(2)创建左孩子 CreateBTreeAccorPT( tnode->LeftChild, //创建左孩子 pP_T + 1, //找到前序遍历序列中左树开始就节点的位置,这里跳过了第一个节点(根)A,得到 "BDCE" pI_T, //中序遍历的节点不需要改动,仍旧是"DBACE" tmpindex ); //(3)创建右孩子 CreateBTreeAccorPT( tnode->RightChild, //创建右孩子 pP_T + tmpindex + 1, //找到前序遍历序列中右树开始节点的位置,这里跳过了tmpindex + 1个节点(根)A,得到 "CE" pI_T + tmpindex + 1, //找到中序遍历序列中右树开始节点的位置,得到的是 "CE" n - tmpindex - 1 //右孩子节点数 ); } }
-
中序与后序遍历创建方式
template<typename T> void BinaryTree<T>::CreateBTreeAccordIPO(char* p_IT, char* pPOST_T) //如何根据中序(左根右)、后续遍历(左右根)序列来创建一颗二叉树 { CreateBTreeAccordIPO(root, p_IT, pPOST_T, strlen(pPOST_T)); } template<typename T> void BinaryTree<T>::CreateBTreeAccordIPO(BinaryTreeNode<T>*& tnode, char* pI_T, char* pPOST_T, int n) { //可以通过后续遍历找到根节点 if (n == 0) { tnode = nullptr; } else { //在中序遍历中找到根,后序遍历序列中根在最后 int tmpindex = 0; //下标 while (pPOST_T[n - 1] != pI_T[tmpindex]) ++tmpindex; tnode = new BinaryTreeNode<T>; //创建根节点 tnode->data = pI_T[tmpindex]; //创建左孩子 CreateBTreeAccordIPO( tnode->LeftChild, //创建左孩子 pI_T, //不需要改动,仍旧是 "DBACE",因为开头的都是左孩子 pPOST_T, //不需要改动,仍旧是"DBECA" tmpindex //左孩子节点数 ); CreateBTreeAccordIPO( tnode->RightChild, //创建右孩子 pI_T + tmpindex + 1, //找到中序遍历中右数开始的位置 "CE" pPOST_T + tmpindex, //找到后序遍历中右树开始的位置,得到的是 "ECA" n - tmpindex - 1 //左孩子节点数 ); } }
测试用例
int main() { BinaryTree<int> tree; //创建一个二叉树 BinaryTreeNode<int>* rootpoint = tree.CreateNode(nullptr, E_Root, 'A'); //创建树根节点 BinaryTreeNode<int>* subpoint = tree.CreateNode(rootpoint, E_ChildLeft, 'B'); //创建左子节点B subpoint = tree.CreateNode(subpoint, E_ChildLeft, 'D'); //创建左子节点B下的左子节点D subpoint = tree.CreateNode(rootpoint, E_ChildRight, 'C'); //创建根的右子节点C subpoint = tree.CreateNode(subpoint, E_ChildRight, 'E'); //创建C的右子节点E //前序遍历序列: ABD###C#E## (给出一个扩展二叉树的前序遍历序列,是能够唯一确定一颗二叉树的) BinaryTree<int> mytree; mytree.CreateBTreeAccorPT((char*)"ABD###C#E##"); cout << "前序遍历序列为: "; mytree.preOrder(); //前序遍历 cout << endl; cout << "中序遍历序列为: "; mytree.inOrder(); //中序遍历 cout << endl; cout << "后序遍历序列为: "; mytree.postOrder(); //后续遍历 cout << endl; cout << "层序遍历序列为: "; mytree.levelOrder(); //层序遍历 cout << endl; cout << "二叉树节点个数为: " << mytree.getSize() << endl; cout << "二叉树的高度为: " << mytree.getHeight() << endl; //查找某个节点 int val = 'B'; BinaryTreeNode<int>* p = mytree.SearchElem(val); if (p != nullptr) { cout << "找到了值为 " << (char)val << " 的节点" << endl; } else { cout << "没找到找到值为 " << (char)val << " 的节点" << endl; } //查找某个节点的父节点 BinaryTreeNode<int>* parp = mytree.GetParent(p); //找到B的父节点 if (parp != nullptr) { cout << "找到了值为 " << (char)val << " 节点的父节点" << (char)parp->data << endl; } else { cout << "没找到找到值为 " << (char)val << " 节点的父节点" << endl; } //测试树的拷贝 cout << "树的拷贝: " << endl; BinaryTree<int> treecopy; tree.CopyTree(&treecopy); treecopy.levelOrder(); cout << endl; cout << "非递归方式前序遍历序列为: "; mytree.prePrder_noRecu(); cout << endl; cout << "非递归方式中序遍历序列为: "; mytree.inOrder_noRecu(); cout << endl; cout << "非递归方式后序遍历序列为: "; mytree.postOrder_noRecu(); cout << endl; cout << "使用前序遍历跟中序遍历创建一个二叉树: "; BinaryTree<int> mytree2; mytree2.CreateBTreeAccordPI((char*)"ABDCE", (char*)"DBACE"); mytree2.levelOrder(); cout << endl; cout << "使用中序遍历跟后序遍历创建一个二叉树: "; BinaryTree<int> mytree3; mytree3.CreateBTreeAccordIPO((char*)"DBACE", (char*)"DBECA"); mytree3.levelOrder(); cout << endl; return 0; }
注意事项
//!!!层序遍历使用到前面介绍的链式队列 //!!!非递归遍历使用了前面介绍的链式栈 //!!!使用g++编译的话,注意 //#include "LinkStack.cc" //#include "LinkQueue.cc" //编译命令 //g++ -o main binary_tree_link.cc LinkStack.cc LinkQueue.cc //上面main是可执行文件名称, binary_tree_link.cc 是本文介绍的文件名
欢迎大家指出博文中的错误哦。

浙公网安备 33010602011771号