题目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行,就指向了根节点。