题目1
根据二叉树的抽象数据类型的定义,使用二叉链表实现一个二叉树。
二叉树的基本功能:
1、二叉树的建立
2、前序遍历二叉树
3、中序遍历二叉树
4、后序遍历二叉树
5、按层序遍历二叉树
6、求二叉树的深度
7、求指定结点到根的路径
8、二叉树的销毁
9、其他:自定义操作
编写测试main()函数测试线性表的正确性。
参考代码:
源.cpp
#include<iostream> #include"标头.h" using namespace std; int main() { int a[13] = { 1,2,3,9,5,6,0,0,0,7 ,0,0,8 }; BiTree<int>tree; tree.Create(tree.Root(), a, 1, 13); cout << "前序遍历:" << endl; tree.PreOrder(tree.Root()); cout << endl; cout << "中序遍历:" << endl; tree.InOrder(tree.Root()); cout << endl; cout << "后序遍历:" << endl; tree.PostOrder(tree.Root()); cout << endl; cout << "层序遍历:" << endl; tree.LevelOrder(tree.Root()); cout << endl; int d = 0, pp; cout << "树的深度:" << endl; pp = tree.GetDepth(tree.Root(), d); cout << pp; cout << endl; cout << "指定节点到根的路径:" << endl; tree.Path(tree.Root(),5); cout << endl; pp=tree.Count(tree.Root()); cout << "二叉树的总结点数:" << endl; cout << pp << endl; system("pause"); return 0;
标头.h
template <class T> struct BiNode { T shuju; BiNode<T> *lchild, *rchild; }; template <class T> class BiTree { protected: BiNode<T> *root; void Release(BiNode<T> *R); public: BiTree() :root(NULL) {} BiNode<T>*& Root(); void Create(BiNode<T> *&R, T data[], int i, int n); void PreOrder(BiNode<T> *R); void InOrder(BiNode<T> *R); void PostOrder(BiNode<T> *R); void LevelOrder(BiNode<T> *R); int GetDepth(BiNode<T> *R, int d); ~BiTree(); bool Path(BiNode<T> *R, T x); int Count(BiNode<T> *R); }; template <class T> BiNode<T>*& BiTree<T>::Root() { return root; } template <class T> void BiTree<T>::Create(BiNode<T> *&R, T data[], int i, int n) { if (i <= n && data[i - 1] != 0) //i表示位置,从1开始计数 { R = new BiNode<T>; R->shuju = data[i - 1]; Create(R->lchild, data, 2 * i, n); Create(R->rchild, data, 2 * i + 1, n); } else R = NULL; } template < class T > void BiTree<T>::PreOrder(BiNode<T> *Root) { if (Root != NULL) { cout << Root->shuju; // 访问结点 PreOrder(Root->lchild); // 遍历左子树 PreOrder(Root->rchild); // 遍历右子树 } } template <class T> bool BiTree<T>::Path(BiNode<T> *root, T data) { if (root == NULL) return false; if (root->shuju == data || Path(root->lchild, data) || Path(root->rchild, data)) { cout << root->shuju; return true; } return false; } template < class T > void BiTree<T>::InOrder(BiNode<T> *Root) { if (Root != NULL) { InOrder(Root->lchild); // 遍历左子树 cout << Root->shuju; // 访问结点 InOrder(Root->rchild); // 遍历右子树 } } template < class T > void BiTree<T>::PostOrder(BiNode<T> *Root) { if (Root != NULL) { PostOrder(Root->lchild); // 遍历左子树 PostOrder(Root->rchild); // 遍历右子树 cout << Root->shuju; // 访问结点 } } template <class T> int BiTree<T>::GetDepth(BiNode<T> *R, int d) { if (R == NULL) return d; if ((R->lchild == NULL) && (R->rchild == NULL)) return d + 1; else { int m = GetDepth(R->lchild, d + 1); int n = GetDepth(R->rchild, d + 1); return n>m ? n : m; } } template <class T> void BiTree<T>::LevelOrder(BiNode<T> *R) { Queue<BiNode<T>*> Q; while (!Q.IsEmpty() || (R != NULL)) { if (R != NULL) { cout << R->shuju; Q.EnQueue(R->lchild); Q.EnQueue(R->rchild); } R = Q.DeQueue(); } } template < class T > BiTree<T>::~BiTree() { Release(root); } template < class T > int BiTree<T>::Count(BiNode<T> *R) { if (R == NULL) return 0; else { int m = Count(R->lchild); int n = Count(R->rchild); return m + n + 1; } } template < class T > void BiTree<T>::Release(BiNode<T> *Root) { if (Root != NULL) { Release(Root->lchild); // 释放左子树 Release(Root->rchild); // 释放右子树 delete Root; // 释放根结点 } } template <class T> class Queue { public: Queue(); int IsEmpty(); void EnQueue(T x); T DeQueue(); ~Queue(); T data; protected: struct Node { T data; Node *next; }; Node *Front; Node *Rear; }; template <class T> Queue<T>::Queue() { Front = new Node; Front->next = NULL; Rear = Front; } template <class T> int Queue<T>::IsEmpty() { if (Front == Rear) return 1; else return 0; } template <class T> void Queue<T>::EnQueue(T t) { Node *s = new Node; s->data = t; s->next = NULL; Rear->next = s; Rear = s; } template <class T> T Queue<T>::DeQueue() { T t = NULL; if (Front == Rear) cout << "队列空,无元素出队!" << endl; else { Node *s = Front->next; t = s->data; Front->next = s->next; delete s; if (Front->next == NULL) Rear = Front; } return t; } template <class T> Queue<T>::~Queue() { while (Front != Rear) { cout << DeQueue() << endl; } }
Q1:为什么Create函数(即创建二叉树的函数)要用指针的引用作为形参?
A1:这要回归到值传递和地址传递的基本问题。我们在实际编程中会遇到这样的情景,传递指针,在形参中修改指针所指向的内容,实参也得到了相应改变。
示例代码1:
#include <iostream> using namespace std; struct Value { int a; int b; }; void modify1(Value *v) { v->a = 10086; v->b = 10010; } int main() { Value *v1 = new Value; v1->a = v1->b = 1; cout << v1->a << " " << v1->b << endl; modify1(v1); cout << v1->a << " " << v1->b<< endl; return 0; }
输出结果:
1 1
10086 10010
但,如果我们想在被调函数中修改指针所存储的内容,即所存地址。而传递的形参是所要修改的指针本身,看看会出现什么错误。
示例代码2:
#include <iostream> using namespace std; struct Value { int a; int b; }; void create(Value *v) { Value *v2 = new Value; v2->a = 10086; v2->b = 10010; v = v2; } int main() { Value *v1 = new Value; v1->a = 1; v1->b = 1; create(v1); cout << v1->a << " " << v1->b << endl; delete v1; return 0; }
输出结果:
1 1
我们可以看到,v1并没有进行相应的赋值操作。
其实回到最根本,原因在于,指针和其他变量一样,拥有自身的地址。与其他变量不同的是,指针变量所存放的数据是地址。指针作为参数进行传递本质上是值传递,只是这个值是地址。通过值传递的方式传递参数,形参的改变不影响实参。示例代码1中,我们传递指针,在被调函数中对指针所指向的内容进行修改,实参中的相应参数成功被修改。示例代码2中,我们传递指针,在被调函数中希望对指针本身进行修改,实参并没有进行相应的操作。这是我们在写代码时应注意的。
将示例代码2修改,得到示例代码3.
示例代码3:
#include <iostream> using namespace std; struct Value { int a; int b; }; void create(Value **v) { (*v)->a = 10086; (*v)->b = 10010; } int main() { Value *v1 = new Value; v1->a = 1; v1->b = 1; create(&v1); cout << v1->a << " " << v1->b << endl; delete v1; return 0; }
输出结果:
10086 10010
总结:将指针归到基本数据类型来理解,和int,char等一样。(这在《mooc浙大数据结构PTA习题之一元多项式的乘法与加法运算》其实有与本问题直接相关的操作。)
回到我们的问题,Create函数的形参用了指针的引用(对指针的引用进行改变能直接改变实参的值,与传递指针的指针有类似的效果),目的是我们在Create中对R的操作能直接改变到实参中的R。对指针本身进行操作,而不是对指针所指向的内容进行操作,要注意形参的形式。
Q2:为什么tree.Root函数获取的永远是指向根节点的指针?
A2:我们看看在创建二叉树时的操作。
源.cpp第10行。
tree.Create(tree.Root(), a, 1, 13);
我们向Create函数传递了还是空指针的root。
标头.h第39行开始
void BiTree<T>::Create(BiNode<T> *&R, T data[], int i, int n) { if (i <= n && data[i - 1] != 0) //i表示位置,从1开始计数 { R = new BiNode<T>; R->shuju = data[i - 1]; Create(R->lchild, data, 2 * i, n); Create(R->rchild, data, 2 * i + 1, n); } else R = NULL; }此时i=1,R指向了根节点,并且在递归函数全部进行完之后,并没有指向根节点的指针R(即root)的操作,因为下面很多的递归中的R指向其他节点,是对其他节点的创建与赋值。root在程序第一次进入Create时(i=1),运行到第43,44行,就指向了根节点。