一、实验目的和要求
目的:熟悉树型结构的建立并实现二叉树深度的求解
要求:两种及以上存储结构(建议 顺序存储结构和链式存储结构各一)、两种及以上方法(建议 递归遍历和层次遍历方法各一)。分析各代码性能。
二、实验环境
编译器:Vscode +DevC++
系统:Windows10
CPU:i5-8265U@1.60GHz
三、实验内容
(1)两种及以上存储结构
(2)两种及以上方法建立
(3)分析四种及以上代码的性能
四、实验过程
4.1 任务定义和问题分析
顺序存储结构
使用一块连续的存储空间存储二叉树
优点:结构紧凑,没有额外的指针空间,随机存取能力强
缺点:插入和删除元素时操作费时,所需空间大时,容易崩溃,不利于对碎片空间的利用
由完全二叉树结点性质可得出结点的序号关系:
编号为i的结点的左儿子的编号为2i(如果存在左结点的话)
编号为i的结点的右儿子的编号为2i+1(如果存在右结点的话)
而非完全二叉树也可以使用上面的规则编号,就是会有许多编号无人认领,造成浪费
链式存储结构
二叉树是一种非线性的结构
使用链式结构能够很直观地表示二叉树的逻辑结构
并且即使二叉树是非二叉树的时候 仍旧能够很好的表示,不会像顺序结构产生空结点
优点:能够很好的利用内存中的碎片空间,提高内存空间的利用效率
缺点:不能随机存取某个结点,需要进行遍历,耗时
递归遍历分为前序遍历,中序遍历,后序遍历三种。
三者复杂度相同
前序遍历:对于当前节点,先输出该节点,然后输出他的左孩子,最后输出他的右孩子。
中序遍历:对于当前结点,先输出它的左孩子,然后输出该结点,最后输出它的右孩子。
后序遍历:对于当前结点,先输出它的左孩子,然后输出它的右孩子,最后输出该结点。
层序遍历:又称广度优先遍历,从名字可以看出和广度优先搜索有关,遍历方式即从根结点开始按层遍历,根结点为第1层,其子结点为第二层,依次从上到下,从左到右遍历。
4.2 数据结构的选择和概要设计
数据结构选择顺序和链式两种
遍历方式选择前序、中序、后序、层序四种
要想降低实验难度,就需要尽量使得结点能够兼容顺序和链式两种,而前序、中序、后序、层序要能兼容顺序和链式两种数据结构
(这里的兼容既可以是接口兼容,也可以是名称兼容)
-
- 详细设计
结点使用一个类来表示,尽量兼容顺序结构和链式结构
类内包含数据域,左儿子指针,右儿子指针。
前序、中序、后序三者的代码主要内容都一样,就是前后遍历顺序不一样,较为容易设计 递归边界是指定结点为空
层序遍历需要利用队列才可顺利运行
而队列的大小在不知道树的大小之前需要尽量设置大一点
在存储结构和遍历方法之间还有一个操作:建树
在实际书写建树时,遇到了很多障碍,我在书写时总是想写成线段树,一度进行不下去,不得已查阅资料,对树进行了复习。建树即是构造好根结点后向此二叉树中插入结点即可。
求二叉树的深度:
深搜----一条路径走到头,然后再拐回来试试其他路径
在书写代码时遇到诸多bug 最后都集中到不能返回结点中的数据这个问题
最后发现是 我使用的是返回临时变量 和&起冲突了
(真·排bug1小时)
五、测试及结果分析
5.1 实验数据
所以深度为3
正确
5.2 结果及分析
在上面已经很好的将两种数据结构的优劣讲清楚了
而遍历方式上,递归遍历需要更多的栈空间
层序遍历需要多的辅助空间,并且代码复杂程度上更大
在无特殊要求情形下,应该使用链式结构+递归遍历来实现二叉树
六、实验收获
熟悉了二叉树的性质于建立
七、参考文献
八、附录(源代码)
#include <cstddef>
#include <iostream>
#include <queue>
#include <stack>
#include <stdlib.h>
using namespace std;
//二叉树结点定义
template<typename DataType>class Bitnode
{
public:
//构建空结点
Bitnode()
{
lchild = NULL;
rchild = NULL;
}
//构建指定数据域的结点
Bitnode(DataType newData)
{
data = newData;
lchild = NULL;
rchild = NULL;
}
//获取数据域
DataType getData()
{
return data;
}
//获取左子结点
Bitnode<DataType>* getLchild()
{
return lchild;
}
//获取右子结点
Bitnode<DataType>* getRchild()
{
return rchild;
}
// private:
DataType data;//数据域
Bitnode *lchild;//左子树指针
Bitnode *rchild;//右子树指针
};
// template <class T>
// struct Bitnode
// {
// Bitnode(const T &data)
// : lchild(NULL), rchild(NULL), data(data)
// {
// }
// Bitnode<T>*getLchild()
// {
// return lchild;
// }
// Bitnode<T> *getRchild()
// {
// return rchild;
// }
// T getData()
// {
// return data;
// }
// Bitnode<T> *lchild;
// Bitnode<T> *rchild;
// T data;
// };
//二叉树线性表示
template <typename DataType>
class SeqBt
{
private:
int maxNode;
DataType *data;
public:
SeqBt(int nodes)
{
if(nodes>0)
{
maxNode = nodes;
data = new DataType[maxNode];
}
}
~SeqBt()
{
delete[] data;
}
};
//二叉树链式表示
template <typename DataType>class Bintree
{
private:
Bitnode<DataType> *root;//设置根结点
public:
Bintree()
{
root = new Bitnode < DataType > ();//初始化根结点
}
//建立有数据的根结点
Bintree(DataType rootData)
{
root = new Bitnode<DataType>(rootData);
root->lchild = NULL;
root->rchild = NULL;
}
Bintree(const DataType *array, size_t size, const DataType &invalid) //构造函数
{
size_t index = 0;
CreateBinTree(root, array, size, index, invalid);
}
void preorder(Bitnode<DataType> *node);
void inorder(Bitnode<DataType> *node);
void postorder(Bitnode<DataType> *node);
void layorder(Bitnode<DataType> *node);
int getDepth(int numberofnodes);
void CreateBinTree(Bitnode<DataType> *&root, const DataType *array, size_t size, size_t &index, const DataType &invalid)
{
if (index < size && invalid != array[index])
{
//根节点
Bitnode<DataType> *newNode = new Bitnode<DataType> (array[index]);
CreateBinTree(newNode->lchild, array, size, ++index, invalid);
CreateBinTree(newNode->rchild, array, size, ++index, invalid);
root = newNode;
}
}
Bitnode<DataType>*getRoot()
{
return root;
}
size_t _Height(Bitnode<DataType>* root) //二叉树的高度
{
if (NULL == root)
return 0;
if (NULL == root->lchild && NULL == root->rchild)
return 1;
return _Height(root->lchild) > _Height(root->rchild) ? _Height(root->lchild) + 1 : _Height(root->rchild) + 1;
}
};
//层序遍历
template <typename DataType>
void Bintree<DataType>::layorder(Bitnode<DataType> *node)
{
int max = 50;
int front, rear;
Bitnode<DataType> *current;
int count = 0;
if(node!=NULL)
{
Bitnode<DataType>* queue[max] ;
front = rear = 0;
queue[rear] = node;
rear++;
count++;
while(count!=0)
{
current = queue[front];
cout << current->getData()<<"\n";
if(current->getLchild()!=NULL)
{
queue[rear] = current->getLchild();
rear = (rear + 1) % max;
count++;
}
if (current->getRchild() != NULL)
{
queue[rear] = current->getRchild();
rear = (rear + 1) % max;
count++;
}
front = (front + 1) % max;
count--;
}
}
}
//求二叉树的深度
template <typename DataType>
int Bintree<DataType>::getDepth(int numberofnodes)
//传入参数结点的个数
{
Bitnode<DataType> *nodestack [numberofnodes];//设置存储二叉树结点的栈
int depthStack[numberofnodes];//声明深度栈
int currentDepth=0, maxDepth = 0;//当前深度,最大深度
int top = -1;//栈的游标指针初始值
Bitnode<DataType> *node = root;//设置当前访问的结点指针并初始化为根结点
if(node!=NULL)//判空
{
currentDepth = 1;//初始化深度为1,根结点为第一层
do//循环遍历整棵树 将访问的结点入栈
//不停反复查找最长的路径
{
while(node!=NULL)
{
nodestack[++top] = node;//将访问到的不为空的结点入栈
depthStack[top] = currentDepth;//将相应的深度入栈
node = node->getLchild();//继续访问当前结点左子结点
currentDepth++;//更新深度
}//走到了当前路径最深处
node = nodestack[top];//出栈栈顶,更换路径
currentDepth = depthStack[top--];
cout << maxDepth << " "<<currentDepth<<"\n";
if(node->getLchild()==NULL&&node->getRchild()==NULL)//是否为叶子结点
if(currentDepth>maxDepth)//更新最大深度
maxDepth = currentDepth;
node = node->getRchild();//左子结点访问完毕 开始访问右子结点
currentDepth++;//更新深度
} while (!(node==NULL&&top==-1));
}
return maxDepth;
}
#include"tree-s.h"
#include<cstdio>
int main()
{
// char pStr[] = {'A', 'B', 'D', '#', '#', '#', 'C', 'E', '#', '#', 'F'};
char pStr[] = {'A', 'B', 'C', 'D'};
Bintree<char> bt1(pStr, sizeof(pStr) / sizeof(pStr[0]), '#');
// Bintree<int> bt1;
cout << "递归前序遍历:" << endl;
// system("pause");
bt1.preorder(bt1.getRoot());
cout << endl;
cout << "递归中序遍历:" << endl;
bt1.inorder(bt1.getRoot());
cout << endl;
cout << "递归后序遍历:" << endl;
bt1.postorder(bt1.getRoot());
cout << endl;
cout << "层次遍历:" << endl;
bt1.layorder(bt1.getRoot());
cout << "\n";
cout << "深度:"<< "\n";
cout << bt1._Height(bt1.getRoot());
cout << "\n";
system("pause");
}