1.实验目的
(1)掌握二叉树的结构特性和二叉链表存储结构。
(2)掌握二叉树遍历的递归和非递归方法。
2.实验内容
(1)假设二叉树采用链接存储方式存储,分别编写一个二叉树先序遍历的递归 算法和非递归算法。
3. 具体实现
3.1 二叉树的生成
3.1.1 构建二叉树结点的结构体
//定义每个结点的结构体
struct biTreeNode
{
//数据域
char data;
//左孩子与右孩子
biTreeNode* leftChild, * rightChild;
};
3.1.2 创建二叉树类来对二叉树进行操作
//构建二叉树类
class BiTree
{
public:
BiTree();
~BiTree();
void preOrder() {
cout << "递归先序遍历二叉树:" ;
preOrder(root);
cout << endl;
}
void npreOrder() {
cout << "非递归先序遍历二叉树:";
npreOrder(root);
cout << endl;
}
private:
biTreeNode* root;
biTreeNode* creatBiTree(biTreeNode* T);
void Release(biTreeNode* T);
void preOrder(biTreeNode* T);
void npreOrder(biTreeNode* T);
};
3.1.2 我们规定二叉树以先序遍历序列(前序遍历)来输入生成二叉树。
若二叉树为空,则空操作返回;否则:
(1)访问根结点
(2)前序遍历根结点的左子树
(3)前序遍历根结点的右子树
注意:此处输入方式规定 '#' 为该结点为空结点
代码如下:
biTreeNode* BiTree::creatBiTree(biTreeNode* T) {
char ch;
cin >> ch;
//如果输入的是'#'则是空结点
if (ch == '#') {
T = NULL;
}
else {
//分配空间
T = new biTreeNode;
//数据域赋值
T->data = ch;
//先序遍历递归赋值
T->leftChild = creatBiTree(T->leftChild);
T->rightChild = creatBiTree(T->rightChild);
}
//返回树的根节点
return T;
}
3.2 递归遍历二叉树
传入二叉树的根节点,与上面构建二叉树的前序遍历法一样进行递归实现
void BiTree::preOrder(biTreeNode* T) {
//递归算法,先序遍历输出
if (T != NULL) {
cout << T->data << ' ';
preOrder(T->leftChild);
preOrder(T->rightChild);
}
}
3.3 非递归遍历二叉树
递归算法中编译器自动生成了栈为我们存储了函数的返回值(我是这么理解的哈哈,希望大佬指正),所以我们在非递归算法中自然而然要用到栈来保存数据。在先序遍历过某结点的整个左子树后,如何找到该结点的右子树的根指针。需要定义一个堆栈存放遍历过的结点 的指针,以便能通过它找到该结点的右子树。而栈就刚好满足了后进先出的条件。
一般情况下,设要遍历的二叉树的根指针为bt,可能有两种情况:
(a)若 bt!=NULL,则表明当前的二叉树不为空,此时,输出根结点 bt 的值并 将 bt 保存到栈中,准备继续遍历 bt 的左子树。
(b)若 bt==NULL,则表明以 bt 为根指针的二叉树遍历完毕,并且 bt 是栈顶指 针所指结点的左子树,若栈不空,则应根据栈顶指针所指结点找到待遍历右子树的根 指针并赋给 bt,以继续遍历下去;若栈空,则表明整个二叉树遍历完毕,应结束。
/*
1. 栈 s 初始化:
1.1 创建biTreeNode* 类型数组;
1.2 栈顶指针top初始化为-1;
2.循环直到 bt 为空且栈 s 为空;
2.1 当 bt 不空时循环
2.1.1 输出 bt->data;
2.1.2 将指针 bt 保存在栈中;
2.1.3 继续遍历 bt 的左子树;
2.2 如果栈 s 不空,则
2.2.1 将栈顶元素弹出至 bt;
2.2.2 准备遍历 bt 的右子树;
*/
void BiTree::npreOrder(biTreeNode* T) {
//非递归先序遍历二叉树算法
biTreeNode* s[100];
int top = -1;
while (T != NULL || top != -1) {
while (T != NULL) {
cout << T->data << ' ';
s[++top] = T;
T = T->leftChild;
}
if (top != -1) {
T = s[top--];
T = T->rightChild;
}
}
}
3.4 完整代码
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
//定义每个结点的结构体
struct biTreeNode
{
//数据域
char data;
//左孩子与右孩子
biTreeNode* leftChild, * rightChild;
};
//构建二叉树类
class BiTree
{
public:
BiTree();
~BiTree();
void preOrder() {
cout << "递归先序遍历二叉树:" ;
preOrder(root);
cout << endl;
}
void npreOrder() {
cout << "非递归先序遍历二叉树:";
npreOrder(root);
cout << endl;
}
private:
biTreeNode* root;
biTreeNode* creatBiTree(biTreeNode* T);
void Release(biTreeNode* T);
void preOrder(biTreeNode* T);
void npreOrder(biTreeNode* T);
};
BiTree::BiTree()
{
cout << "请按照先序遍历的顺序输入二叉树的元素,空结点用'#'代替" << endl;
//返回二叉树的根节点
root = creatBiTree(root);
}
BiTree::~BiTree()
{
//销毁空间
Release(root);
}
void BiTree::Release(biTreeNode* T) {
//递归算法,后序遍历销毁空间
if (T != NULL) {
Release(T->leftChild);
Release(T->rightChild);
delete T;
}
}
biTreeNode* BiTree::creatBiTree(biTreeNode* T) {
char ch;
cin >> ch;
//如果输入的是'#'则是空结点
if (ch == '#') {
T = NULL;
}
else {
//分配空间
T = new biTreeNode;
//数据域赋值
T->data = ch;
//先序遍历递归赋值
T->leftChild = creatBiTree(T->leftChild);
T->rightChild = creatBiTree(T->rightChild);
}
//返回树的根节点
return T;
}
void BiTree::preOrder(biTreeNode* T) {
//递归算法,先序遍历输出
if (T != NULL) {
cout << T->data << ' ';
preOrder(T->leftChild);
preOrder(T->rightChild);
}
}
/*
1. 栈 s 初始化:
1.1 创建biTreeNode* 类型数组;
1.2 栈顶指针top初始化为-1;
2.循环直到 bt 为空且栈 s 为空;
2.1 当 bt 不空时循环
2.1.1 输出 bt->data;
2.1.2 将指针 bt 保存在栈中;
2.1.3 继续遍历 bt 的左子树;
2.2 如果栈 s 不空,则
2.2.1 将栈顶元素弹出至 bt;
2.2.2 准备遍历 bt 的右子树;
*/
void BiTree::npreOrder(biTreeNode* T) {
//非递归先序遍历二叉树算法
biTreeNode* s[100];
int top = -1;
while (T != NULL || top != -1) {
while (T != NULL) {
cout << T->data << ' ';
s[++top] = T;
T = T->leftChild;
}
if (top != -1) {
T = s[top--];
T = T->rightChild;
}
}
}
void test();
void test() {
BiTree bi;
bi.preOrder();
bi.npreOrder();
system("pause");
}
int main() {
test();
}
通过此次实验,加深了我对递归算法的熟练度,之前真的看到递归就烦。。。