二叉树的详细实现 (C++)

Posted windsun

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了二叉树的详细实现 (C++)相关的知识,希望对你有一定的参考价值。

二叉树的定义

    以递归形式给出的:一棵二叉树是结点的一个有限集合,该集合或者为空,或者是由一个根结点加上两棵分别称为左子树和右子树的、互不相交的二叉树组成。二又树的特点是每个结点最多有两个子女,分别称为该结点的左子女和右子女。在二又树中不存在度大于2的结点,并且二又树的子树有左、右之分,其子树的次序不能颠倒。二又树是分支数最大不超过2的有根有序树。它可能有5种不同的形态。
技术图片

二叉树的性质

 技术图片

二叉树的数组存储方式

 技术图片

    遇到空子树,应在编号时假定有此子树进行编号,而在顺序存储时当作有此子树那样把位置留出来。这样才能反映二叉树结点之间的相互关系,由其存储位置找到它的父结点、子女、兄弟结点的位置。但这样做有可能会消耗大量的存储空间。例如:单支二叉树,会浪费很多空间。

 技术图片
如果根节点编号是从1开始有有以下结论:
    中间节点一定在倒数第二层,最后一个节点的数就是总节点的个数,总结点数除2就是中间节点的数的个数,父节点的节点数*2<总节点个数,当前节点一定有两个孩子,如果=就只有一个孩子,如果<就没有一个孩子。

二叉树的链表存储表示

 技术图片

二叉树结点类型的定义

1 template<typename T>
2 struct BinTreeNode
3 {
4     T data;    //结点中存储的数据
5     BinTreeNode<T> *leftChild, *rightChild;    //左子树和右子树
6     BinTreeNode() :leftChild(NULL), rightChild(NULL) {}    //无参构造函数
7     BinTreeNode(T x, BinTreeNode<T> *l = NULL, BinTreeNode<T> *r = NULL) :data(x), leftChild(l), rightChild(r) {}    //带默认值的有参构造参数
8 };

二叉树的基本框架

  1 template<typename T>
  2 class BinaryTree
  3 {
  4 public:
  5     //构造函数
  6     BinaryTree() :root(NULL) {}
  7     //指定结束标志的构造函数
  8     BinaryTree(T value) :RefValue(value), root(NULL) {}
  9     //复制构造函数(前序遍历的应用)
 10     BinaryTree(BinaryTree<T>& s)
 11     {
 12         root = Copy(s.getRoot());
 13     }
 14     //判断两颗二叉树是否相等(前序遍历的应用)
 15     bool operator==(BinaryTree<T>& s)
 16     {
 17         return (equal(this->getRoot(), s.getRoot()));
 18     }
 19     //析构函数
 20     ~BinaryTree() { Destroy(root); }
 21     //创建二叉树(使用广义表创建)
 22     void CreateBinTree() { CreateBinTree(root); }
 23     //创建二叉树(利用二叉树的前序遍历创建)(前序遍历的应用)
 24     void CreateBinTree_PreOrder() { CreateBinTree_PreOrder(root); }
 25     //二叉树中序遍历(递归)
 26     void InOrder() { InOrder(root); }
 27     //二叉树先序遍历(递归)
 28     void PreOrder() { PreOrder(root); }
 29     //二叉树先序遍历(非递归1)
 30     void PreOrder_NoRecurve1() { PreOrder_NoRecurve1(root); }
 31     //二叉树先序遍历(非递归2)
 32     void PreOrder_NoRecurve2() { PreOrder_NoRecurve2(root); }
 33     //二叉树后序遍历(递归)
 34     void PostOrder() { PostOrder(root); }
 35     //二叉树的层次遍历(非递归遍历)
 36     void LevelOrder() { LevelOrder(root); }
 37     //二叉树的中序遍历(非递归)
 38     void InOrder_NoRecurve() { InOrder_NoRecurve(root); }
 39     //计算二叉树的结点的个数(后序遍历的应用)
 40     int Size() { return Size(root); }
 41     //计算二叉树的高度(后序遍历的应用)
 42     int Height() { return Height(root); }
 43     //销毁函数
 44     void Destroy() { Destroy(root); }
 45     //返回current结点的父节点
 46     BinTreeNode<T>* Parent(BinTreeNode<T>* current)
 47     {
 48         return (root == NULL || root == current) ? NULL : Parent(root, current);
 49     }
 50     //判断二叉树是否为空
 51     bool Empty() { return (root == NULL) ? true : false; }
 52     //返回current结点的左结点
 53     BinTreeNode<T>* LeftChild(BinTreeNode<T>* current)
 54     {
 55         return (current != NULL) ? current->leftChild : NULL;
 56     }
 57     //返回current结点的右结点
 58     BinTreeNode<T>* RightChild(BinTreeNode<T>* current)
 59     {
 60         return (current != NULL) ? current->rightChild : NULL;
 61     }
 62     //获取二叉树的根节点
 63     BinTreeNode<T>* getRoot() const
 64     {
 65         return root;
 66     }
 67     //以广义表的形式输出二叉树(前序遍历的应用)
 68     void PrintBinTree() { PrintBinTree(root); }
 69     //二叉树的后序遍历(非递归)
 70     void PostOrder_NoRecurve() { PostOrder_NoRecurve(root); }
 71 
 72 protected:
 73     //使用广义表创建二叉树函数,这里以“字符”创建二叉树,以‘#‘字符代表结束
 74     void CreateBinTree(BinTreeNode<T>* &BT);
 75     //二叉树的中序遍历
 76     void InOrder(BinTreeNode<T> *&subTree);
 77     //二叉树的先序遍历
 78     void PreOrder(BinTreeNode<T> *&subTree);
 79     //二叉树的后序遍历
 80     void PostOrder(BinTreeNode<T> *&subTree);
 81     //计算二叉树以subTree为根的结点的个数
 82     int Size(BinTreeNode<T> *subTree) const;
 83     //计算二叉数以subTree为根的结点的个数
 84     int Height(BinTreeNode<T> *subTree);
 85     //二叉树的销毁函数
 86     void Destroy(BinTreeNode<T> *&subTree);
 87     //从结点subTree开始,搜索结点current的父节点,找到返回父节点的地址,找不到返回NULL
 88     BinTreeNode<T>* Parent(BinTreeNode<T>* subTree, BinTreeNode<T>* current);
 89     //复制构造函数调用的Copy函数,返回一个指针,给出一个以originNode为根的二叉树的副本
 90     BinTreeNode<T>* Copy(BinTreeNode<T>* originNode);
 91     //判断两颗二叉树是否相等
 92     bool equal(BinTreeNode<T> *a, BinTreeNode<T> *b);
 93     //创建二叉树(利用已知的二叉树的前序遍历创建)用#表示空结点
 94     void CreateBinTree_PreOrder(BinTreeNode<T>* &subTree);
 95     //以广义表的形式输出二叉树
 96     void PrintBinTree(BinTreeNode<T> *BT);
 97     //二叉树先序遍历(非递归1)
 98     void PreOrder_NoRecurve1(BinTreeNode<T> *p);
 99     //二叉树先序遍历(非递归2)
100     void PreOrder_NoRecurve2(BinTreeNode<T> *p);
101     //二叉树的层次遍历(非递归遍历)
102     void LevelOrder(BinTreeNode<T> *p);
103     //二叉树的中序遍历(非递归)
104     void InOrder_NoRecurve(BinTreeNode<T>* p);
105     //二叉树的后序遍历(非递归)
106     void PostOrder(BinTreeNode<T>* p);
107     
108 private:
109     BinTreeNode<T>* root;    //根节点
110     T RefValue;    //数据输入停止的标志,需要一个构造函数
111 };

 

二叉树的创建

  1.使用广义表创建

从广义表A(B(D,E(G,)),C(,F))# 建立起来的二叉树。
 技术图片
算法基本思路:
  1.若是字母(假定以字母作为结点的值),则表示是结点的值,为它建立一个新的结点,并把该结点作为左子女(当k=1)或右子女(当k=2)链接到其父结点上。
  2.若是左括号"(",则表明子表的开始,将k置为1;若遇到的是右括号")",则表明子表结束。
  3.若遇到的是逗号",",则表示以左子女为根的子树处理完毕,应接着处理以右子女为根的子树,将k置为2。如此处理每一个字符,直到读入结束符“#”为止。
在算法中使用了一个栈s,在进入子表之前将根结点指针进栈,以便括号内的子女链接之用。在子表处理结束时退栈。
 
 1     //使用广义表创建二叉树函数,这里以“字符”创建二叉树,以‘#‘字符代表结束
 2     void CreateBinTree(BinTreeNode<T>* &BT)
 3     {
 4         stack< BinTreeNode<T>* > s;
 5         BT = NULL;
 6         BinTreeNode<T> *p, *t;    //p用来记住当前创建的节点,t用来记住栈顶的元素
 7         int k;    //k是处理左、右子树的标记
 8         T ch;
 9         cin >> ch;
10 
11         while (ch != RefValue)
12         {
13             switch (ch)
14             {
15             case (:    //对(做处理
16                 s.push(p);
17                 k = 1;
18                 break;
19 
20             case ):    //对)做处理
21                 s.pop();
22                 break;
23 
24             case ,:    //对,做处理
25                 k = 2;
26                 break;
27 
28             default:
29                 p = new BinTreeNode<T>(ch);    //构造一个结点
30                 if (BT == NULL)    //如果头节点是空
31                 {
32                     BT = p;
33                 }
34                 else if (k == 1)    //链入*t的左孩子
35                 {
36                     t = s.top();
37                     t->leftChild = p;
38                 }
39                 else    //链入*t的右孩子
40                 {
41                     t = s.top();
42                     t->rightChild = p;
43                 }
44             }
45             cin >> ch;
46         }
47     }

   2.使用已知的二叉树的前序遍历创建

  必须对应二又树结点前序遍历的顺序,并约定以输入序列中不可能出现的值作为空结点的值以结束递归,此值通过构造函数存放在RefValue中。例如用“#”或表示字符序列或正整数序列空结点。
 技术图片
  前序遍历所得到的前序序列为ABC##DE#G##F###。
算法的基本思想是:
  每读入一个值,就为它建立结点。该结点作为根结点,其地址通过函数的引用型参数subTree直接链接到作为实际参数的指针中。然后,分别对根的左、右子树递归地建立子树,直到读入“#”建立空子树递归结束。
 
 1     //创建二叉树(利用已知的二叉树的前序遍历创建)用#表示空结点
 2     void CreateBinTree_PreOrder(BinTreeNode<T>* &subTree)
 3     {
 4         T item;
 5         if (cin >> item)
 6         {
 7             if (item != RefValue)
 8             {
 9                 subTree = new BinTreeNode<T>(item);    //构造结点
10                 if (subTree == NULL)
11                 {
12                     cout << "空间分配错误!" << endl;
13                     exit(1);
14                 }
15                 CreateBinTree_PreOrder(subTree->leftChild);    //递归创建左子树
16                 CreateBinTree_PreOrder(subTree->rightChild);    //递归创建右子树
17             }
18             else
19             {
20                 subTree == NULL;
21             }
22         }
23     }

 二叉树的递归遍历

  先序遍历:根->左->右

 1     //二叉树的先序遍历
 2     void PreOrder(BinTreeNode<T> *&subTree)
 3     {
 4         if (subTree != NULL)
 5         {
 6             cout << subTree->data << " ";
 7             PreOrder(subTree->leftChild);
 8             PreOrder(subTree->rightChild);
 9         }
10     }

  中序遍历:左->根->右

 1     //二叉树的中序遍历
 2     void InOrder(BinTreeNode<T> *&subTree)
 3     {
 4         if (subTree != NULL)
 5         {
 6             InOrder(subTree->leftChild);
 7             cout << subTree->data << " ";
 8             InOrder(subTree->rightChild);
 9         }
10     }

  后续遍历:左->右->根

 1     //二叉树的后序遍历
 2     void PostOrder(BinTreeNode<T> *&subTree)
 3     {
 4         if (subTree != NULL)
 5         {
 6             PostOrder(subTree->leftChild);
 7             PostOrder(subTree->rightChild);
 8             cout << subTree->data << " ";
 9         }
10     }

 二叉树的非递归遍历

  先序遍历

  为了把一个递归过程改为非递归过程,一般需要利用一个工作栈,记录遍历时的回退路径。
 技术图片

  利用栈实现前序遍历的过程。每次访问一个结点后,在向左子树遍历下去之前,利用这个栈记录该结点的右子女(如果有的话)结点的地址,以便在左子树退回时可以直接从栈顶取得右子树的根结点,继续其右子树的前序遍历。

 
 1     //二叉树先序遍历(非递归1)
 2     void PreOrder_NoRecurve1(BinTreeNode<T> *p)
 3     {
 4         stack<BinTreeNode<T>*> S;
 5         S.push(NULL);    //最先push一个NULL,到最后一个结点没有左右子树时,栈里只有一个NULL了,令指针p指向这个NULL,再判断就会结束循环
 6         while (p!=NULL)
 7         {
 8             cout << p->data << " ";
 9             if(p->rightChild!=NULL)    //预留右子树指针在栈中
10             {
11                 S.push(p->rightChild);
12             }
13 
14             if (p->leftChild!=NULL)    //进左子树
15             {
16                 p = p->leftChild;
17             }
18             else    //左子树为空
19             {
20                 p = S.top();
21                 S.pop();
22             }
23         }
24     }

     另一种前序遍历的方法。为了保证先左子树后右子树的顺序,在进栈时是先进右子女结点地址,后进左子女结点地址,出栈时正好相反。

 1     //二叉树先序遍历(非递归2)
 2     void PreOrder_NoRecurve2(BinTreeNode<T> *p)
 3     {
 4         stack<BinTreeNode<T>*> S;
 5         BinTreeNode<T>* t;
 6         S.push(p);    //根节点进栈
 7         while (!S.empty())    //当栈不为空
 8         {
 9             t = S.top();    //p先记住栈顶元素,然后栈顶出栈
10             S.pop();
11             cout << t->data << " ";    //访问元素
12             if (t->rightChild != NULL)    //右孩子不为空,右孩子近栈
13             {
14                 S.push(t->rightChild);
15             }
16             if (t->leftChild != NULL)    //左孩子不为空,左孩子进栈
17             {
18                 S.push(t->leftChild);
19             }
20         }
21     }

   中序遍历

  需要使用一个栈,以记录遍历过程中回退的路径。在一棵子树中首先访问的是中序下的第一个结点,它位于从根开始沿leftChild链走到最左下角的结点,该结点的leftChild指针为NULL。访问它的数据之后,再遍历该结点的右子树。此右子树又是二叉树,重复执行上面的过程,直到该子树遍历完。
 技术图片
  如果某结点的右子树遍历完或右子树为空,说明以这个结点为根的二叉树遍历完,此时从栈中退出更上层的结点并访问它,再向它的右子树遍历下去。
 1     //二叉树的中序遍历(非递归)
 2     void InOrder_NoRecurve(BinTreeNode<T>* p)
 3     {
 4         stack<BinTreeNode<T>*> S;
 5         do
 6         {
 7             while (p!=NULL)
 8             {
 9                 S.push(p);
10                 p = p->leftChild;
11             }
12             if (!S.empty())
13             {
14                 p = S.top();
15                 S.pop();
16                 cout << p->data << " ";
17                 p = p->rightChild;
18             }
19         }
20         while (p!=NULL||!S.empty());
21     }

  后续遍历

  后序遍历比前序和中序遍历的情况复杂。在遍历完左子树时还不能访问根结点,需要再遍历右子树。待右子树遍历完后才访问根结点。所以在栈工作记录中必须注明刚才是在左子树(L)还是在右子树(R)中。
  在后序遍历中所用栈的结点定义:
1 //后续遍历(非递归)所用的注明左右的结点的类型
2 template<typename T>
3 struct stkNode
4 {
5     BinTreeNode<T> *ptr;    //指向树结点的指针
6     enum tag { L, R };    //该结点退栈的标记
7     stkNode(BinTreeNode<T> *N = NULL) :ptr(N), tag(L) {}    //带默认值的有参构造函数
8 };

  算法思想:首先使用栈暂存根结点地址,再向左子树遍历下去,此时根结点的tag=L。当访问完左子树中结点并从左子树退回时,还要去遍历根的右子树,此时改根结点的tag=R。在从右子树中退出时才访问位于栈顶的根结点的值。

 技术图片
 1     //二叉树的后序遍历(非递归)
 2     void PostOrder(BinTreeNode<T>* p)
 3     {
 4         stack<stkNode<T>> S;    //创建一个栈
 5         stkNode<T> w;    //创建一个注明左右类型的结点
 6         do
 7         {
 8             while (p != NULL)    //循环将左结点压栈
 9             {
10                 w.ptr = p;
11                 w.tag = L;
12                 S.push(w);
13                 p = p->leftChild;
14             }
15             int flag = 1;    //默认flag为1
16             while (flag&&S.empty())
17             {
18                 w = S.top();    //记住栈顶元素并出栈
19                 S.pop();
20                 p = w.ptr;    //并使p指向记住的栈顶元素中存储的结点
21                 switch (w.tag)
22                 {
23                     case L:        //若标记为左,就将标志改为右,再进栈
24                         w.tag = R;
25                         S.push(w);
26                         flag = 0;    //flag置为0
27                         p = p->rightChild;
28                         break;
29                     case R:        //若标记为右孩子
30                         cout << p->data << " ";
31                         break;
32                 }
33             }
34         }
35         while (!S.empty());
36         cout << endl;
37     }

  层次遍历

    按层次顺序访问二叉树的处理需要利用一个队列。在访问二又树的某一层结点时,把下一层结点指针预先记忆在队列中,利用队列安排逐层访问的次序。因此,每当访问一个结点时,将它的子女依次加到队列的队尾,然后再访问已在队列队头的结点。这样可以实现二又树结点的按层访问。
 技术图片
 1     //二叉树的层次遍历(非递归遍历)
 2     void LevelOrder(BinTreeNode<T> *p)
 3     {
 4         queue<BinTreeNode<T>*> Q;
 5         Q.push(p);    //根节点进队
 6         BinTreeNode<T>* t;
 7         while (!Q.empty())
 8         {
 9             t = Q.front();    //t先记住队头,再将队头出队
10             Q.pop();
11             cout << t->data << " ";    //访问队头元素的数据
12 
13             if (t->leftChild != NULL)
14             {
15                 Q.push(t->leftChild);
16             }
17 
18             if (t->rightChild != NULL)
19             {
20                 Q.push(t->rightChild);
21             }
22         }
23     }

 二叉树的结点个数

1     //计算二叉树以subTree为根的结点的个数
2     int Size(BinTreeNode<T> *subTree) const
3     {
4         if (subTree == NULL)    //递归结束,空树结点个数为0
5         {
6             return 0;
7         }
8         return 1 + Size(subTree->leftChild) + Size(subTree->rightChild);
9     }

二叉树的高度

 1     //计算二叉数以subTree为根的高度
 2     int Height(BinTreeNode<T> *subTree)
 3     {
 4         if (subTree == NULL)    //递归结束,空树高度为0
 5         {
 6             return 0;
 7         }
 8         int i = Height(subTree->leftChild);
 9         int j = Height(subTree->rightChild);
10         return i < j ? j + 1 : i + 1;
11     }

 以广义表的形式输出二叉树

 1     void PrintBinTree(BinTreeNode<T> *BT)
 2     {
 3         if (BT != NULL)    //树为空时结束递归
 4         {
 5             cout << BT->data;
 6             if (BT->leftChild != NULL || BT->rightChild != NULL)
 7             {
 8                 cout << (;
 9                 if (BT->leftChild!=NULL)
10                 {
11                     PrintBinTree(BT->leftChild);
12                 }
13                 cout << ,;
14                 if (BT->rightChild != NULL)
15                 {
16                     PrintBinTree(BT->rightChild);
17                 }
18                 cout << );
19             }
20         }
21     }

求二叉树某结点的父节点

 1     //从结点subTree开始,搜索结点current的父节点,找到返回父节点的地址,找不到返回NULL
 2     BinTreeNode<T>* Parent(BinTreeNode<T>* subTree, BinTreeNode<T>* current)
 3     {
 4         if (subTree == NULL)
 5         {
 6             return NULL;
 7         }
 8         if (subTree->leftChild == current || subTree->rightChild == current)    //如果找到,返回父节点subTree
 9         {
10             return subTree;
11         }
12         BinTreeNode<T>* p;
13         if (p = Parent(subTree->leftChild, current) != NULL)    //递归在左子树中搜索
14         {
15             return p;
16         }
17         else
18         {
19             return Parent(subTree->rightChild, current);    //递归右子树中搜索
20         }
21     }

二叉树的销毁

 1     //二叉树的销毁函数
 2     void Destroy(BinTreeNode<T> *&subTree)
 3     {
 4         if (subTree != NULL)
 5         {
 6             Destroy(subTree->leftChild);
 7             Destroy(subTree->rightChild);
 8             delete subTree;
 9             subTree = NULL;
10         }
11     }

 判断两颗二叉树是否相等

 1     //判断两颗二叉树是否相等
 2     bool equal(BinTreeNode<T> *a, BinTreeNode<T> *b)
 3     {
 4         if (a == NULL&&b == NULL)    //两者都为空
 5         {
 6             return true;
 7         }
 8         if (a != NULL&&b != NULL&&a->data == b->data&&equal(a->leftChild, b->leftChild) && equal(a->rightChild, b->rightChild))    //两者都不为空,且两者的结点数据相等,且两者的左右子树的结点都相等
 9         {
10             return true;
11         }
12         return false;
13     }

 

 完整代码:
技术图片二叉树
  1 //结点类型
  2 template<typename T>
  3 struct BinTreeNode
  4 {
  5     T data;    //结点中存储的数据
  6     BinTreeNode<T> *leftChild, *rightChild;    //左子树和右子树
  7     BinTreeNode() :leftChild(NULL), rightChild(NULL) {}    //无参构造函数
  8     BinTreeNode(T x, BinTreeNode<T> *l = NULL, BinTreeNode<T> *r = NULL) :data(x), leftChild(l), rightChild(r) {}    //带默认值的有参构造参数
  9 };
 10 
 11 //后续遍历(非递归)所用的注明左右的结点的类型
 12 template<typename T>
 13 struct stkNode
 14 {
 15     BinTreeNode<T> *ptr;    //指向树结点的指针
 16     enum tag { L, R };    //该结点退栈的标记
 17     stkNode(BinTreeNode<T> *N = NULL) :ptr(N), tag(L) {}    //带默认值的有参构造函数
 18 };
 19 
 20 //二叉树类
 21 template<typename T>
 22 class BinaryTree
 23 {
 24 public:
 25     //构造函数
 26     BinaryTree() :root(NULL) {}
 27     //指定结束标志的构造函数
 28     BinaryTree(T value) :RefValue(value), root(NULL) {}
 29     //复制构造函数(前序遍历的应用)
 30     BinaryTree(BinaryTree<T>& s)
 31     {
 32         root = Copy(s.getRoot());
 33     }
 34     //判断两颗二叉树是否相等(前序遍历的应用)
 35     bool operator==(BinaryTree<T>& s)
 36     {
 37         return (equal(this->getRoot(), s.getRoot()));
 38     }
 39     //析构函数
 40     ~BinaryTree() { Destroy(root); }
 41     //创建二叉树(使用广义表创建)
 42     void CreateBinTree() { CreateBinTree(root); }
 43     //创建二叉树(利用二叉树的前序遍历创建)(前序遍历的应用)
 44     void CreateBinTree_PreOrder() { CreateBinTree_PreOrder(root); }
 45     //二叉树中序遍历(递归)
 46     void InOrder() { InOrder(root); }
 47 
 48     //二叉树先序遍历(递归)
 49     void PreOrder() { PreOrder(root); }
 50     //二叉树先序遍历(非递归1)
 51     void PreOrder_NoRecurve1() { PreOrder_NoRecurve1(root); }
 52     //二叉树先序遍历(非递归2)
 53     void PreOrder_NoRecurve2() { PreOrder_NoRecurve2(root); }
 54 
 55     //二叉树后序遍历(递归)
 56     void PostOrder() { PostOrder(root); }
 57 
 58     //二叉树的层次遍历(非递归遍历)
 59     void LevelOrder() { LevelOrder(root); }
 60 
 61 
 62     //二叉树的中序遍历(非递归)
 63     void InOrder_NoRecurve() { InOrder_NoRecurve(root); }
 64 
 65     //计算二叉树的结点的个数(后序遍历的应用)
 66     int Size() { return Size(root); }
 67     //计算二叉树的高度(后序遍历的应用)
 68     int Height() { return Height(root); }
 69 
 70     //销毁函数
 71     void Destroy() { Destroy(root); }
 72     //返回current结点的父节点
 73     BinTreeNode<T>* Parent(BinTreeNode<T>* current)
 74     {
 75         return (root == NULL || root == current) ? NULL : Parent(root, current);    //如果没有根节点或current结点就是root结点,就没有父节点
 76     }
 77     //判断二叉树是否为空
 78     bool Empty() { return (root == NULL) ? true : false; }
 79     //返回current结点的左结点
 80     BinTreeNode<T>* LeftChild(BinTreeNode<T>* current)
 81     {
 82         return (current != NULL) ? current->leftChild : NULL;
 83     }
 84     //返回current结点的右结点
 85     BinTreeNode<T>* RightChild(BinTreeNode<T>* current)
 86     {
 87         return (current != NULL) ? current->rightChild : NULL;
 88     }
 89     //获取二叉树的根节点
 90     BinTreeNode<T>* getRoot() const
 91     {
 92         return root;
 93     }
 94     //以广义表的形式输出二叉树(前序遍历的应用)
 95     void PrintBinTree() { PrintBinTree(root); }
 96     //二叉树的后序遍历(非递归)
 97     void PostOrder_NoRecurve() { PostOrder_NoRecurve(root); }
 98 
 99 
100 protected:
101     //使用广义表创建二叉树函数,这里以“字符”创建二叉树,以‘#‘字符代表结束
102     void CreateBinTree(BinTreeNode<T>* &BT)
103     {
104         stack< BinTreeNode<T>* > s;
105         BT = NULL;
106         BinTreeNode<T> *p, *t;    //p用来记住当前创建的节点,t用来记住栈顶的元素
107         int k;    //k是处理左、右子树的标记
108         T ch;
109         cin >> ch;
110 
111         while (ch != RefValue)
112         {
113             switch (ch)
114             {
115             case (:    //对(做处理
116                 s.push(p);
117                 k = 1;
118                 break;
119 
120             case ):    //对)做处理
121                 s.pop();
122                 break;
123 
124             case ,:    //对,做处理
125                 k = 2;
126                 break;
127 
128             default:
129                 p = new BinTreeNode<T>(ch);    //构造一个结点
130                 if (BT == NULL)    //如果头节点是空
131                 {
132                     BT = p;
133                 }
134                 else if (k == 1)    //链入*t的左孩子
135                 {
136                     t = s.top();
137                     t->leftChild = p;
138                 }
139                 else    //链入*t的右孩子
140                 {
141                     t = s.top();
142                     t->rightChild = p;
143                 }
144             }
145             cin >> ch;
146         }
147     }
148 
149     //二叉树的中序遍历
150     void InOrder(BinTreeNode<T> *&subTree)
151     {
152         if (subTree != NULL)
153         {
154             InOrder(subTree->leftChild);
155             cout << subTree->data << " ";
156             InOrder(subTree->rightChild);
157         }
158     }
159 
160     //二叉树的先序遍历
161     void PreOrder(BinTreeNode<T> *&subTree)
162     {
163         if (subTree != NULL)
164         {
165             cout << subTree->data << " ";
166             PreOrder(subTree->leftChild);
167             PreOrder(subTree->rightChild);
168         }
169     }
170 
171     //二叉树的后序遍历
172     void PostOrder(BinTreeNode<T> *&subTree)
173     {
174         if (subTree != NULL)
175         {
176             PostOrder(subTree->leftChild);
177             PostOrder(subTree->rightChild);
178             cout << subTree->data << " ";
179         }
180     }
181 
182     //计算二叉树以subTree为根的结点的个数
183     int Size(BinTreeNode<T> *subTree) const
184     {
185         if (subTree == NULL)    //递归结束,空树结点个数为0
186         {
187             return 0;
188         }
189         return 1 + Size(subTree->leftChild) + Size(subTree->rightChild);
190     }
191 
192     //计算二叉数以subTree为根的高度
193     int Height(BinTreeNode<T> *subTree)
194     {
195         if (subTree == NULL)    //递归结束,空树高度为0
196         {
197             return 0;
198         }
199         int i = Height(subTree->leftChild);
200         int j = Height(subTree->rightChild);
201         return i < j ? j + 1 : i + 1;
202     }
203 
204     //二叉树的销毁函数
205     void Destroy(BinTreeNode<T> *&subTree)
206     {
207         if (subTree != NULL)
208         {
209             Destroy(subTree->leftChild);
210             Destroy(subTree->rightChild);
211             delete subTree;
212             subTree = NULL;
213         }
214     }
215 
216     //从结点subTree开始,搜索结点current的父节点,找到返回父节点的地址,找不到返回NULL
217     BinTreeNode<T>* Parent(BinTreeNode<T>* subTree, BinTreeNode<T>* current)
218     {
219         if (subTree == NULL)
220         {
221             return NULL;
222         }
223         if (subTree->leftChild == current || subTree->rightChild == current)    //如果找到,返回父节点subTree
224         {
225             return subTree;
226         }
227         BinTreeNode<T>* p;
228         if (p = Parent(subTree->leftChild, current) != NULL)    //递归在左子树中搜索
229         {
230             return p;
231         }
232         else
233         {
234             return Parent(subTree->rightChild, current);    //递归右子树中搜索
235         }
236     }
237 
238     //复制构造函数调用的Copy函数,返回一个指针,给出一个以originNode为根的二叉树的副本
239     BinTreeNode<T>* Copy(BinTreeNode<T>* originNode)
240     {
241         if (originNode == NULL)
242         {
243             return NULL;
244         }
245         BinTreeNode<T>* temp = new BinTreeNode<T>;    //创建根结点
246         temp->data = originNode->data;
247         temp->leftChild = Copy(originNode->leftChild);
248         temp->rightChild = Copy(originNode->rightChild);
249         return temp;
250 
251     }
252     //判断两颗二叉树是否相等
253     bool equal(BinTreeNode<T> *a, BinTreeNode<T> *b)
254     {
255         if (a == NULL&&b == NULL)    //两者都为空
256         {
257             return true;
258         }
259         if (a != NULL&&b != NULL&&a->data == b->data&&equal(a->leftChild, b->leftChild) && equal(a->rightChild, b->rightChild))    //两者都不为空,且两者的结点数据相等,且两者的左右子树的结点都相等
260         {
261             return true;
262         }
263         return false;
264     }
265     //创建二叉树(利用已知的二叉树的前序遍历创建)用#表示空结点
266     void CreateBinTree_PreOrder(BinTreeNode<T>* &subTree)
267     {
268         T item;
269         if (cin >> item)
270         {
271             if (item != RefValue)
272             {
273                 subTree = new BinTreeNode<T>(item);    //构造结点
274                 if (subTree == NULL)
275                 {
276                     cout << "空间分配错误!" << endl;
277                     exit(1);
278                 }
279                 CreateBinTree_PreOrder(subTree->leftChild);    //递归创建左子树
280                 CreateBinTree_PreOrder(subTree->rightChild);    //递归创建右子树
281             }
282             else
283             {
284                 subTree == NULL;
285             }
286         }
287     }
288     //以广义表的形式输出二叉树
289     void PrintBinTree(BinTreeNode<T> *BT)
290     {
291         if (BT != NULL)    //树为空时结束递归
292         {
293             cout << BT->data;
294             if (BT->leftChild != NULL || BT->rightChild != NULL)
295             {
296                 cout << (;
297                 if (BT->leftChild!=NULL)
298                 {
299                     PrintBinTree(BT->leftChild);
300                 }
301                 cout << ,;
302                 if (BT->rightChild != NULL)
303                 {
304                     PrintBinTree(BT->rightChild);
305                 }
306                 cout << );
307             }
308         }
309     }
310     //二叉树先序遍历(非递归1)
311     void PreOrder_NoRecurve1(BinTreeNode<T> *p)
312     {
313         stack<BinTreeNode<T>*> S;
314         S.push(NULL);    //最先push一个NULL,到最后一个结点没有左右子树时,栈里只有一个NULL了,令指针p指向这个NULL,再判断就会结束循环
315         while (p!=NULL)
316         {
317             cout << p->data << " ";
318             if(p->rightChild!=NULL)    //预留右子树指针在栈中
319             {
320                 S.push(p->rightChild);
321             }
322 
323             if (p->leftChild!=NULL)    //进左子树
324             {
325                 p = p->leftChild;
326             }
327             else    //左子树为空
328             {
329                 p = S.top();
330                 S.pop();
331             }
332         }
333     }
334     //二叉树先序遍历(非递归2)
335     void PreOrder_NoRecurve2(BinTreeNode<T> *p)
336     {
337         stack<BinTreeNode<T>*> S;
338         BinTreeNode<T>* t;
339         S.push(p);    //根节点进栈
340         while (!S.empty())    //当栈不为空
341         {
342             t = S.top();    //p先记住栈顶元素,然后栈顶出栈
343             S.pop();
344             cout << t->data << " ";    //访问元素
345             if (t->rightChild != NULL)    //右孩子不为空,右孩子近栈
346             {
347                 S.push(t->rightChild);
348             }
349             if (t->leftChild != NULL)    //左孩子不为空,左孩子进栈
350             {
351                 S.push(t->leftChild);
352             }
353         }
354     }
355 
356     //二叉树的层次遍历(非递归遍历)
357     void LevelOrder(BinTreeNode<T> *p)
358     {
359         queue<BinTreeNode<T>*> Q;
360         Q.push(p);    //根节点进队
361         BinTreeNode<T>* t;
362         while (!Q.empty())
363         {
364             t = Q.front();    //t先记住队头,再将队头出队
365             Q.pop();
366             cout << t->data << " ";    //访问队头元素的数据
367 
368             if (t->leftChild != NULL)
369             {
370                 Q.push(t->leftChild);
371             }
372 
373             if (t->rightChild != NULL)
374             {
375                 Q.push(t->rightChild);
376             }
377         }
378     }
379 
380     //二叉树的中序遍历(非递归)
381     void InOrder_NoRecurve(BinTreeNode<T>* p)
382     {
383         stack<BinTreeNode<T>*> S;
384         do
385         {
386             while (p!=NULL)
387             {
388                 S.push(p);
389                 p = p->leftChild;
390             }
391             if (!S.empty())
392             {
393                 p = S.top();
394                 S.pop();
395                 cout << p->data << " ";
396                 p = p->rightChild;
397             }
398         }
399         while (p!=NULL||!S.empty());
400     }
401 
402     //二叉树的后序遍历(非递归)
403     void PostOrder(BinTreeNode<T>* p)
404     {
405         stack<stkNode<T>> S;    //创建一个栈
406         stkNode<T> w;    //创建一个注明左右类型的结点
407         do
408         {
409             while (p != NULL)    //循环将左结点压栈
410             {
411                 w.ptr = p;
412                 w.tag = L;
413                 S.push(w);
414                 p = p->leftChild;
415             }
416             int flag = 1;    //默认flag为1
417             while (flag&&S.empty())
418             {
419                 w = S.top();    //记住栈顶元素并出栈
420                 S.pop();
421                 p = w.ptr;    //并使p指向记住的栈顶元素中存储的结点
422                 switch (w.tag)
423                 {
424                     case L:        //若标记为左,就将标志改为右,再进栈
425                         w.tag = R;
426                         S.push(w);
427                         flag = 0;    //flag置为0
428                         p = p->rightChild;
429                         break;
430                     case R:        //若标记为右孩子
431                         cout << p->data << " ";
432                         break;
433                 }
434             }
435         }
436         while (!S.empty());
437         cout << endl;
438     }
439 private:
440     BinTreeNode<T>* root;    //根节点
441     T RefValue;    //数据输入停止的标志,需要一个构造函数
442 };

 测试函数:

技术图片主函数测试
 1 int main(int argc, char* argv[])
 2 {
 3     //A(B(D,E(G,)),C(,F))#
 4     //abc##de#g##f###
 5     BinaryTree<char> tree(#);    //调用以#号结束的构造函数
 6     tree.CreateBinTree();
 7 
 8     cout << "中序遍历:";
 9     tree.InOrder();
10     cout << endl;
11 
12     cout << "先序遍历:";
13     tree.PreOrder();
14     cout << endl;
15 
16     cout << "后序遍历:";
17     tree.PostOrder();
18     cout << endl;
19 
20     cout << "二叉树的结点的个数是:" << tree.Size() << endl;
21     cout << "二叉树的高度是:" << tree.Height() << endl;
22     
23     BinaryTree<char> tree1(tree);
24     cout << "tree和tree1是否相等:";
25     cout << (tree==tree1)<<endl;
26 
27     //使用前序遍历创建二叉树
28     BinaryTree<char> tree2(#);
29     tree2.CreateBinTree_PreOrder();
30 
31     cout << "先序遍历:";
32     tree2.PreOrder();
33 
34     cout << "先序遍历非递归1:";
35     tree2.PreOrder_NoRecurve1();
36 
37     cout << "先序遍历非递归2:";
38     tree2.PreOrder_NoRecurve2();
39 
40     cout << "层次遍历:";
41     tree2.LevelOrder();
42 
43     cout << "中序遍历非递归:";
44     tree2.InOrder_NoRecurve();
45 
46     cout << "\\n广义表输出二叉树:";
47     tree2.PrintBinTree();
48     return 0;
49 }

 

技术图片

以上是关于二叉树的详细实现 (C++)的主要内容,如果未能解决你的问题,请参考以下文章

二叉树的前序中序后序层次遍历的原理及C++代码实现

精选力扣500题 第35题 LeetCode 94. 二叉树的中序遍历c++ / java 详细题解

精选力扣500题 第13题 LeetCode 102. 二叉树的层序遍历c++详细题解

精选力扣500题 第19题 LeetCode 199. 二叉树的右视图c++详细题解

精选力扣500题 第10题 LeetCode 103. 二叉树的锯齿形层序遍历 c++详细题解

C++二叉树的实现