个人主页:
专栏:
目录
二叉树
基本概念
树的基本术语
结点的度:子结点或非空子树的个数
树的度:树中所有结点的度的最大值
叶结点:树中度为0的结点
中间结点:树中叶结点以外的结点,亦称内部结点
兄弟结点:父结点相同的结点彼此是兄弟结点
结点的层次:根结点在第1层;如果结点的层次是𝑘 (𝑘≥1) ,则其子结点都在第𝑘+1层。亦称结点的深度
结点的高度:叶结点的高度等于1;中间结点的高度等于其所有子结点的高度的最大值加1
树的高度:根结点的高度,亦称树的深度
有序树:树中各结点的子树从左向右依次排列,不能交换次序;否则称作无序树
森林:零个或多个互不相交(独立)的树的集合
二叉树的定义
二叉树(Binary Tree):
二叉树是一种树形数据结构,其中每个节点最多有两个子节点,通常称为左子节点和右子节点,因此二叉树的度为2。二叉树可以为空,或者由一个根节点和零个、一个或两个子树组成,这些子树也是二叉树。
二叉树的定义可以用递归的方式描述:
- 一个节点包含一个数据元素以及指向其左子树和右子树的指针。
- 左子树和右子树也是二叉树。
- 二叉树的子树可以为空,也可以包含一个或多个节点。
二叉树可以是有序的,也可以是无序的,它被广泛应用于表达式计算,数据压缩和编码,字符串处理和搜索中。
二叉树的形态
基本形态
从上面二叉树的递归定义可以看出,二叉树或为空,或为一个根结点加上两棵左右子树,因为两棵左右子树也是二叉树也可以为空,所以二叉树有5种基本形态:
特殊形态
完全二叉树
完全二叉树满足下述三个条件:
1.从第1层到第𝑑−2层全是度为2的中间结点
2.第𝑑层的结点都是叶结点,度为0
3.在第𝑑−1层,各结点的度从左向右单调非递增排列,同时度为1的结点要么没有,要么只有一个且该结点的左子树非空
下图为树的高度不超过3的所有完全二叉树
满二叉树
由度为0的叶结点和度为2的中间结点构成的二叉树,树中没有度为1的结点(国外定义)
国内定义满二叉树等同上文所述的完全二叉树
完美二叉树
对于高度(深度)为d≥1 的完全二叉树,如果第d-1层所有结点的度都是2,则该树是一个完美二叉树
扩充二叉树
扩充二叉树是二叉树中的一种,是指在二叉树中出现空子树的位置增加空树叶,所形成的二叉树
二叉树的常见基本操作
-
插入节点(Insert Node):在二叉树中插入新的节点,通常需要遵循特定的插入规则,如二叉搜索树中保持节点值的有序性。
-
删除节点(Delete Node):从二叉树中删除指定节点,需要考虑不同情况下的处理方法,如叶子节点、有一个子节点的节点、有两个子节点的节点等。
-
查找节点(Search Node):在二叉树中查找指定值的节点,可以采用递归或迭代的方式实现。
-
遍历(Traversal):遍历二叉树是指按照一定顺序访问树中的所有节点。常见的遍历方式包括:
-
前序遍历(Preorder Traversal):根-左-右
-
中序遍历(Inorder Traversal):左-根-右
-
后序遍历(Postorder Traversal):左-右-根
-
层序遍历(Level Order Traversal):按层从左到右逐个访问节点
-
-
计算树的高度(Calculate Height):计算二叉树的高度或深度,即树中从根节点到最远叶子节点的最长路径的长度。
-
判断是否为空树(IsEmpty):检查二叉树是否为空,即是否包含任何节点。
-
判断树的相等性(Check Tree Equality):比较两棵二叉树是否结构相同且对应节点的值相等
二叉树的基本性质
1.二叉树的第i层上至多有2^(i-2)(i≥1)个节点
2.深度为h的二叉树中至多含有2^(h-1)个节点
3.若在任意一棵二叉树中,有n0个叶子节点,有n2个度为2的节点,则必有n0=n2+1
4.具有n个节点的满二叉树深为log2(n+1)
5.非空满二叉树中叶结点数等于中间结点数加1(满二叉树定理)
6.完全二叉树有n个结点(n≥1),按层次从左向右连续编号。树中任一结点k (1≤k≤n)满足以下性质:
(1)如果2k≤n,则结点 k 的左子结点是 2k,否则没有左子结点
(2)如果2k+1≤n ,则结点 k 的右子结点是2k+1 ,否则没有右子结点
(3)如果 k>1,则结点 k 的父结点是 ⌊k/2⌋
二叉树的存储方式
顺序存储
完全二叉树顺序存储
完全二叉树所有结点可以分层从左向右连续编号,可用一组地址连续的存储单元(顺序表)存储二叉树的各个结点
非完全二叉树的顺序存储
(1)结点编号:树根的索引为1;设结点的编号为𝑘 (𝑘≥1),如果其左子树非空,则左子结点的编号为2𝑘;如果右子树非空,则右子结点为2𝑘+1
(2)顺序存放:用一组地址连续的存储单元存储二叉树的各个结点
链接存储
使用链表存放二叉树,比顺序存储能更有效地表达二叉树的非线性逻辑结构
二叉树类的应用
下面让我们自己动手用递归的方法实现一棵二叉树,并完成求高度,规模以及遍历等操作。
递归实现二叉树
问题描述
创建两棵由非负整型值组成的二叉树 tree0 和 tree1,求出这两棵二叉树的高度及规模(节点数)并将他们归并为一棵新的树 tree2,求新的二叉树的高度、规模以及该树的前序、中序和后序遍历结果。
输入
第一行输入 tree0 的数据;
第二行输入 tree1 的数据;
第三行输入 tree2 根节点的数据
- 备注:
二叉树的创建过程为先输入根结点的值,创建根节点;对已添加到树上的每个结点,依次输入它的两个儿子的值。如果没有儿子,则输入特定值-1 作为标志。循环上述操作直至二叉树创建完成,下图为一个二叉树创建实例: - 二叉树的归并过程为将第三行输入的数据作为 tree2 的根节点,tree0 根节点成为 tree2 根节点的左节点,tree1 根节点成为 tree2根节点的右节点。
输出
第一行输出 tree0 的高度与规模;
第二行输出 tree1 的高度与规模;
第三行输出 tree2 的高度与规模;
第四行输出 tree2 的前序遍历结果;
第五行输出 tree2 的中序遍历结果;
第六行输出 tree2 的后序遍历结果
- 备注:
注意输出最后一行数据后即结束程序运行,不做换行处理。
样例
输入
1 2 3 4 -1 5 6 -1 -1 7 -1 -1 -1 -1 -1
3 2 1 0 -1 -1 -1 0 -1 0 -1 -1 -1
10
输出
4 7
5 6
6 14
10 1 2 4 3 5 7 6 3 2 0 0 0 1
4 2 1 7 5 3 6 10 0 0 0 2 3 1
4 2 7 5 6 3 1 0 0 0 2 1 3 10
解题思路
该问题创建两棵二叉树,求出树的高度和规模并且归并两棵树,输出归并后的前序、中序和后序遍历结果。由于树的特性,我们可以采用递归算法来求解树的高度、规模以及三种遍历结果,归并树时只需创建一个新节点,该结点的左结点和右结点分别指向两棵树的根节点即可。
代码实现
结点类(Node)
首先是树的结点设计,可以设计出下图所示结构体,Node 表示结点,其中包含有数据域data和左指针域left以及右指针域right,用于指向孩子结点。
其中 data 表示数据,其可以是简单的类型,也可以是复杂的结构体,故采用泛型编程template<typename Type>。左右指针表示,左右孩子,其指向下一个结点。结点类还有构造函数,在创建结点时可以进行初始化。
template <class Type>
struct Node {
Node* left;
Node* right;
Type data;
Node() :left(NULL), right(NULL) {}
Node(Type item, Node* L = NULL, Node* R = NULL) :data(item), left(L), right(R) {}
~Node() {}
};
二叉树类(Binary Tree)
二叉树类(Binary Tree)采用泛型编程,其中Type是模板参数,代表树中元素的类型。root为树的根结点,利用根结点可对整个树进行增删查改操作。
公有函数
含有一些基本函数或者通过公有函数调用私有函数,完成查询树根节点、高度和规模等功能
构造函数
构造函数用于初始化根节点,将root初始化为 NULL,表示树为空。
基本函数
getRoot() : 返回根节点的值
getLeft(): 返回左子节点的值
getRight() : 返回右子节点的值
Node* getroot() : 返回指向根节点的指针
int size() const { return size(root); }: 返回二叉树的节点数(规模)
int height() const { return height(root); }: 返回二叉树的高度。
preOrder(),midOrder(),postOrder():用于调用私有的前序、中序、后序遍历函数
template <class Type>
class BinaryTree {
public:
BinaryTree() :root(NULL) {}
BinaryTree(const Type& value) {
root = new Node(value);
}
Type getRoot() const { return root->data; }
Type getLeft() const { return root->left->data; }
Type getRight() const { return root->right->data; }
Node* getroot() { return root; }
int size() const { return size(root); }
int height() const { return height(root); }
}
void preOrder() const {
if (root != NULL) {
preOrder(root);
}
cout << endl;
}
void midOrder() const {
if (root != NULL) {
midOrder(root);
}
cout << endl;
}
void postOrder() const {
if (root != NULL) {
postOrder(root);
}
//cout << endl;
}
};
createTree()函数
用于创建一个二叉树,并根据输入的数据构建该二叉树的结构。
该函数首先定义一个队列 que,用于辅助构建二叉树,从标准输入中读取第一个节点的值 x,并检查其是否为标记值,如果是则将根节点置为空,结束函数。
如果根节点值不是标记值,则创建根节点,并将其加入队列中。
使用队列进行层次遍历:从队列中取出一个节点 tmp,并移出队列。 从标准输入中读取该节点的左子节点值 ldata 和右子节点值 rdata。 如果左子节点值不是标记值,则创建一个新节点,并将其设为当前节点 tmp 的左子节点,同时将新节点加入队列。 如果右子节点值不是标记值,则创建一个新节点,并将其设为当前节点 tmp 的右子节点,同时将新节点加入队列。 循环直到队列为空,即所有节点都已经处理完毕
void createTree(float flag) {
queue<Node*> que;
Node* tmp;
Type x, ldata, rdata;
cin >> x;
if (x == flag) {
root = NULL;
return;
}
root = new Node(x);
que.push(root);
while (!que.empty())
{
tmp = que.front();
que.pop();
cin >> ldata >> rdata;
if (ldata != flag)
que.push(tmp->left = new Node(ldata));
if (rdata != flag)
que.push(tmp->right = new Node(rdata));
}
}
height()函数
递归求树高,如果当前节点不为空,则递归计算左子树和右子树的高度,并取较大值,返回 1(当前节点的高度)加上左右子树中较大的高度
int height(Node* t) const {
if (t == NULL) return 0;
else {
int lt = height(t->left), rt = height(t->right);
return 1 + ((lt > rt) ? lt : rt);
}
}
size()函数
递归求节点数(规模),如果当前节点不为空,则递归计算左子树和右子树的规模,并相加,返回 1(当前节点的规模)加上左右子树的节点数
int size(Node * t) const {
if (t == NULL) return 0;
return 1 + size(t->left) + size(t->right);
}
遍历函数
preOrder()函数
对给定二叉树进行前序遍历,并将遍历结果输出到标准输出流中。如果当前节点指针不为空,则先输出当前节点的值,然后递归地对左子树和右子树进行前序遍历
midOrder 函数
对给定二叉树进行中序遍历,并将遍历结果输出到标准输出流中。如果当前节点指针不为空,则先递归地对左子树进行中序遍历,然后输出当前节点的值,最后递归地对右子树进行中序遍历。
postOrder 函数
对给定二叉树进行后序遍历,并将遍历结果输出到标准输出流中。 如果当前节点指针不为空,则先递归地对左子树进行后序遍历,然后递归地对右子树进行后序遍历,最后输出当前节点的值。
void preOrder(Node * t) const {
if (t != NULL) {
cout << t->data << ' ';
preOrder(t->left);
preOrder(t->right);
}
}
void midOrder(Node * t) const {
if (t != NULL) {
midOrder(t->left);
cout << t->data << ' ';
midOrder(t->right);
}
}
void postOrder(Node * t) const {
if (t != NULL) {
postOrder(t->left);
postOrder(t->right);
cout << t->data << ' ';
}
}
makeTree()函数
创建一棵新的二叉树,并将两棵已有的二叉树作为其左子树和右子树
创建一个新的根节点,左节点指向tree1的根结点,右节点指向tree2的根结点
void makeTree(const Type& x, BinaryTree& lt, BinaryTree& rt) {
root = new Node(x);
root->left = lt.getroot();
root->right = rt.getroot();
}
clear()函数
用于清空树的所有节点
如果输入的根节点指针 t 为 NULL,则表示当前节点为空,直接返回,不做任何操作。 递归清空左子树和右子树,即先清空左子树,再清空右子树。 在清空左右子树后,删除当前节点 t,释放其内存
void clear(Node * t) {
if (t->left != NULL)
clear(t->left);
if (t->right != NULL)
clear(t->right);
delete t;
//t = NULL;
return;
}
完整代码(递归法)
#include <queue>
#include <stack>
#include <iostream>
using namespace std;
template <class Type>
class BinaryTree {
private:
struct Node {
Node* left;
Node* right;
Type data;
Node() :left(NULL), right(NULL) {}
Node(Type item, Node* L = NULL, Node* R = NULL) :data(item), left(L), right(R) {}
~Node() {}
};
Node* root;
public:
BinaryTree() :root(NULL) {}
BinaryTree(const Type& value) {
root = new Node(value);
}
Type getRoot() const { return root->data; }
Type getLeft() const { return root->left->data; }
Type getRight() const { return root->right->data; }
Node* getroot() { return root; }
void makeTree(const Type& x, BinaryTree& lt, BinaryTree& rt) {
root = new Node(x);
root->left = lt.getroot();
root->right = rt.getroot();
}
int size() const { return size(root); }
int height() const { return height(root); }
void preOrder() const {
if (root != NULL) {
preOrder(root);
}
cout << endl;
}
void midOrder() const {
if (root != NULL) {
midOrder(root);
}
cout << endl;
}
void postOrder() const {
if (root != NULL) {
postOrder(root);
}
//cout << endl;
}
// 此处输入等于 flag 表示该位置没有节点的情况
void createTree(float flag) {
queue<Node*> que;
Node* tmp;
Type x, ldata, rdata;
cin >> x;
if (x == flag) {
root = NULL;
return;
}
root = new Node(x);
que.push(root);
while (!que.empty())
{
tmp = que.front();
que.pop();
cin >> ldata >> rdata;
if (ldata != flag)
que.push(tmp->left = new Node(ldata));
if (rdata != flag)
que.push(tmp->right = new Node(rdata));
}
}
private:
int height(Node* t) const { if (t == NULL) return 0;
else {
int lt = height(t->left), rt = height(t->right);
return 1 + ((lt > rt) ? lt : rt);
}
}
void clear(Node * t) {
if (t->left != NULL)
clear(t->left);
if (t->right != NULL)
clear(t->right);
delete t;
//t = NULL;
return;
}
int size(Node * t) const {
if (t == NULL) return 0;
return 1 + size(t->left) + size(t->right);
}
//递归实现
void preOrder(Node * t) const {
if (t != NULL) {
cout << t->data << ' ';
preOrder(t->left);
preOrder(t->right);
}
}
//递归实现
void midOrder(Node * t) const {
if (t != NULL) {
midOrder(t->left);
cout << t->data << ' ';
midOrder(t->right);
}
}
//递归实现
void postOrder(Node * t) const {
if (t != NULL) {
postOrder(t->left);
postOrder(t->right);
cout << t->data << ' ';
}
}
};
int main() {
int x;
BinaryTree<int> tree, tree1, tree2;
//第一颗树的构建,-1 作为没有子节点的标志
tree.createTree(-1);
cout << tree.height() << ' ' << tree.size() << endl;
//第二颗树的构建
tree1.createTree(-1);
cout << tree1.height() << ' ' << tree1.size() << endl;
//合并两棵树,x 为根节点的值
cin >> x;
tree2.makeTree(x, tree, tree1);
cout << tree2.height() << ' ' << tree2.size() << endl;
tree2.preOrder();
tree2.midOrder();
tree2.postOrder();
return 0;
}
递归算法的问题
(1) 存在不支持递归算法的程序设计语言
(2) 递归算法在运行中,需要系统在内存栈中分配空间保存函数的参数、返回地址以及局部变量等,运行效率较低
(3)系统对每个进程分配的栈容量有限,如果二叉树的深度太大造成递归调用的层次太高,容易导致栈溢出
下面我们采用非递归算法实现二叉树,即用迭代的方法实现二叉树,关键是使用栈结构模拟函数调用中系统栈的工作原理
二叉树遍历迭代器
问题描述
二叉树的遍历可以使用递归算法实现, 但是递归算法的执行速度较慢, 而非递归的实现比较复杂。 现在希望使用二叉树的迭代器来实现二叉树的三种遍历(即, 前序遍历、 中序遍历和后序遍历),三种遍历对应于三种迭代器。
输入
第一行输入二叉树的节点数据, 用来创建一棵二叉树, 其输入逻辑类同实验“二叉树类的应用”。
- 备注:
1)二叉树的创建过程为先输入根结点的值, 创建根节点;
2)对已添加到树上的每个结点, 依次输入它的两个儿子的值。 3)如果没有儿子, 则输入特定值-1 作为标志。
循环上述操作直至二叉树创建完成,下图为一个二叉树创建实例:
输出
输出一共三行, 分别表示前、 中、 后序遍历结果, 每行整数间使用空格分开。
- 备注:
输出最后一行数据后即结束程序运行,不做换行处理。
样例
输入:
1 2 3 -1 -1 4 -1 -1 -1
输出:
1 2 3 4
2 1 4 3
2 4 3 1
解题思路
使用二叉树的迭代器来实现二叉树的三种遍历(前序遍历、中序遍历和后序遍历)可以通过栈来实现。
前序遍历迭代器
创建一个栈,将根节点压入栈中。 循环直到栈为空
(1)弹出栈顶节点,并输出其值
(2)如果栈顶节点有右子节点,则将右子节点压入栈中
(3)如果栈顶节点有左子节点,则将左子节点压入栈中
中序遍历迭代器
创建一个栈,将根节点及其所有左子节点压入栈中, 循环直到栈为空
(1)弹出栈顶节点,并输出其值
(2)如果弹出的节点有右子节点,则将右子节点及其所有左子节点压入栈中
后序遍历迭代器
创建两个栈,一个用于存储遍历结果,另一个用于辅助遍历。 将根节点压入第一个栈中。 循环直到第一个栈为空
(1)弹出栈顶节点,将其值插入第二个栈的栈顶
(2)如果弹出的节点有左子节点,则将左子节点压入第一个栈中
(3)如果弹出的节点有右子节点,则将右子节点压入第一个栈中
(4)最后输出第二个栈的所有元素即为后序遍历结果。
代码实现
树结点类(Node)
和递归方法的树结点基本相同,不再赘述
struct Node {
Node* left;
Node* right;
Type data;
Node() :left(NULL), right(NULL) {}
Node(Type item, Node* L = NULL, Node* R = NULL) :data(item), left(L), right(R) {}
};
辅助结点类(StNode)
StNode 是用来辅助实现后序遍历的迭代器中的结构体。
(1)StNode 结构体存储了二叉树节点的指针信息以及辅助计数信息
(2)TimesPop 成员变量用于记录节点被弹出栈的次数
(3)StNode 结构体主要用于后序遍历迭代器中,通过记录节点被弹出栈的次数,可以判断何时访问节点,并确定节点的访问顺序。
template <class Type>
struct StNode
{
Node<Type>* node;
int TimesPop;
StNode(Node<Type>* N = NULL) : node(N), TimesPop(0) {}
};
链式栈类(linkStack)
该链式栈类实现了栈的基本功能,包括入栈、出栈、返回栈顶元素、判断栈是否为空以及清空栈。由于采用链表作为底层数据结构,可以动态地管理内存空间
栈的详解可阅读:手把手教数据结构与算法:栈的应用(平衡符号和简单计算器)_计算器系统 栈-CSDN博客
template <class elemType>
class linkStack : public stack<elemType>
{
private:
struct node {
elemType data;
node* next;
node(const elemType& x, node* N = NULL) { data = x; next = N; }
node() :next(NULL) {}
~node() {}
};
//结点定义 next和数据
node* elem;
public:
linkStack() { elem = NULL; }
~linkStack() {
node* tmp;
while (elem != NULL) {
tmp = elem;
elem = elem->next;
delete tmp;
}
}
bool isEmpty() { return elem == NULL; }
void push(const elemType& x) {
node* tmp = new node(x, elem);
elem = tmp;
}
// 入栈
elemType pop() {
node* tmp = elem;
elemType x = tmp->data;
elem = elem->next;
delete tmp;
return x;
}
// 出栈 并返回头结点数据
elemType top() { return elem->data; }
void makeEmpty() {
node* tmp;
while (elem != NULL) {
tmp = elem;
elem = elem->next;
delete tmp;
}
elem = NULL;
}
// 清空栈
};
TreeIterator类
TreeIterator 类提供一个通用的接口,用于实现二叉树的迭代器功能。通过派生类实现不同的遍历方式的 First() 和 operator++() 函数,可以实现前序、中序和后序遍历等不同的遍历方式。
template <class Type>
class TreeIterator {
public:
TreeIterator(const BinaryTree<Type>& BT) : T(BT), current(NULL) {}
virtual ~TreeIterator() {}
// 第一个被访问的结点地址送 current
virtual void First() = 0;
// 下一个被访问的结点地址送 current
virtual void operator++() = 0;
// 判当前结点为空吗, 为空返回 True
bool operator+() const { return current != NULL; }
// 返回当前结点指针 current 所指向的结点的数据值。
Type& operator()() const { return current->data; }
public:
const BinaryTree<Type>& T;
// BinaryTree<Type>::Node* current;
Node<Type>* current;
// 指向当前结点的指针。
};
前序遍历(Preorder)
成员函数 First重写了基类中的纯虚函数 First(),用于定位到第一个被访问的节点。在该函数中,首先清空栈 s,然后将二叉树的根节点压入栈 s 中,最后调用 operator++() 函数。
成员函数 operator++
重写了基类中的纯虚函数 operator++(),用于定位到下一个被访问的节点。
(1)在该函数中,首先判断栈 s 是否为空,如果为空则将当前节点指针 current 置为空并返回;(2)如果栈不为空,则弹出栈顶节点,并将其指定为当前节点。然后,如果当前节点有右子节点,则将右子节点压入栈 s 中;如果当前节点有左子节点,则将左子节点压入栈 s 中。
template <class Type>
class Preorder : public TreeIterator<Type> //前序遍历迭代器
{
public:
Preorder(const BinaryTree<Type>& R) : TreeIterator<Type>(R) { s.push(this->T.root); }
~Preorder() {}
void First() {
s.makeEmpty();
if (this->T.root) s.push(this->T.root);
operator++();
}
void operator++() {
if (s.isEmpty()) {
this->current = NULL; return;
}
this->current = s.top();
s.pop();
// 得到当前结点的地址, 并进行出栈操作。
if (this->current->right != NULL)
s.push(this->current->right); //非空右儿子进栈。
if (this->current->left != NULL)
s.push(this->current->left); //非空左儿子进栈。
}
protected:
linkStack<Node<Type>*> s;
};
中序遍历(InOrder)
(1)首先,检查栈 s 是否为空,如果为空,说明已经完成了中序遍历,将当前节点指针 current 置为 NULL 并返回。
(2)如果栈不为空,则从栈 s 中弹出一个节点 Cnode。 对弹出的节点 Cnode 进行操作:
如果节点 Cnode 的遍历状态计数器 TimesPop 的值为 2,表示左子树已经被处理,当前节点可以被访问:将当前节点指针 current 指向节点 Cnode。 如果节点 Cnode 存在右子节点,则将右子节点压入栈 s 中,为下一次迭代做准备。 返回,结束函数执行。
如果节点 Cnode 的遍历状态计数器 TimesPop 的值不为 2,表示左子树还未处理,需要继续处理:将节点 Cnode 再次压入栈 s 中,表示暂时不访问当前节点,继续处理其左子树。 如果节点 Cnode 存在左子节点,则将其左子节点压入栈 s 中,为下一次迭代做准备。
template <class Type> class Inorder : public Postorder < Type >
{
public:
Inorder(const BinaryTree < Type >& R) : Postorder<Type>(R) { }
void operator++(); // 中序时的下一个结点的地址。
};
template <class Type>
void Inorder<Type>::operator++()
{
if (this->s.isEmpty())
{ // 当栈空且 current 也为空时, 遍历结束。
this->current = NULL; return;
} // 置当前指针为空, 结束。
StNode<Type> Cnode;
for (; ; )
{
Cnode = this->s.top(); this->s.pop();
if (++Cnode.TimesPop == 2) // 左子树已处理,该结点可访问。
{
this->current = Cnode.node;
if (Cnode.node->right != NULL) // 有右儿子,进栈。
this->s.push(StNode<Type>(Cnode.node->right));
return;
}
this->s.push(Cnode);
if (Cnode.node->left != NULL)
this->s.push(StNode<Type>(Cnode.node->left));
}
}
后序遍历(PostOrder)
(1)检查栈是否为空: 如果栈 s 为空,则表示遍历已经结束,将当前节点指针 current 置为空,并返回。
(2)弹出栈顶节点: 从栈 s 中弹出一个节点 Cnode。 更新节点的遍历状态: 对 Cnode 的遍历状态计数器 TimesPop 进行增加操作,表示当前节点被访问的次数。
(3)判断节点是否可访问: 如果 Cnode 的遍历状态计数器为 3,则表示当前节点已经完成了左右子树的访问,可以被访问。将当前节点指针 current 指向 Cnode.node,即当前节点。 返回,结束函数执行。
(4)根据节点的遍历状态进行入栈操作: 如果 Cnode 的遍历状态计数器为 1,则表示当前节点的左子树还未访问,需要访问左子树。如果当前节点有左子节点,则将其压入栈 s 中,同时将遍历状态计数器设为 2,表示左子树访问结束。 如果 Cnode 的遍历状态计数器为 2,则表示当前节点的左子树已经访问完毕,需要访问右子树。如果当前节点有右子节点,则将其压入栈 s 中,同时将遍历状态计数器设为 3,表示右子树访问结束。
template <class Type>
class Postorder : public TreeIterator<Type>
{
public:
Postorder(const BinaryTree < Type >& R) : TreeIterator<Type>(R) {
s.push(StNode<Type>(this->T.root));
}
~Postorder() { }
// 后序遍历时的第一个结点的地址。
void First() {
s.makeEmpty();
if (this->T.root) s.push(this->T.root);
operator++();
}
// 后序遍历时的下一个结点的地址。
void operator++() {
if (s.isEmpty())
{ // 当栈空且 current 也为空时, 遍历结束。
this->current = NULL;
return;
} // 置当前指针为空,结束。
StNode<Type> Cnode;
for (; ; )
{
{ Cnode = s.top(); s.pop();
if (++Cnode.TimesPop == 3) //该结点可访问。
{
this->current = Cnode.node; return;
}
s.push(Cnode);
if (Cnode.TimesPop == 1) // 转向访问左子树。
{
if (Cnode.node->left != NULL) // 有左儿子,进栈。
s.push(StNode<Type>(Cnode.node->left));
}
else {// Cnode.TimesPop == 2 访左结束,转向访问右子树。
if (Cnode.node->right != NULL)
s.push(StNode<Type>(Cnode.node->right));
}
}
}
}
protected:
linkStack<StNode<Type>> s;
};
完整代码(迭代器)
#include <queue>
#include <stack>
#include <iostream>
using namespace std;
template <typename Type>
struct Node {
Node* left;
Node* right;
Type data;
Node() :left(NULL), right(NULL) {}
Node(Type item, Node* L = NULL, Node* R = NULL) :data(item), left(L), right(R) {}
};
//结点定义 左右子和数据
template <class Type>
struct StNode
{
Node<Type>* node;
int TimesPop;
StNode(Node<Type>* N = NULL) : node(N), TimesPop(0) {}
};
template <class elemType>
class linkStack : public stack<elemType>
{
private:
struct node {
elemType data;
node* next;
node(const elemType& x, node* N = NULL) { data = x; next = N; }
node() :next(NULL) {}
~node() {}
};
//结点定义 next和数据
node* elem;
public:
linkStack() { elem = NULL; }
~linkStack() {
node* tmp;
while (elem != NULL) {
tmp = elem;
elem = elem->next;
delete tmp;
}
}
bool isEmpty() { return elem == NULL; }
void push(const elemType& x) {
node* tmp = new node(x, elem);
elem = tmp;
}
// 入栈
elemType pop() {
node* tmp = elem;
elemType x = tmp->data;
elem = elem->next;
delete tmp;
return x;
}
// 出栈 并返回头结点数据
elemType top() { return elem->data; }
void makeEmpty() {
node* tmp;
while (elem != NULL) {
tmp = elem;
elem = elem->next;
delete tmp;
}
elem = NULL;
}
// 清空栈
};
template <class Type>
class BinaryTree {
public:
Node<Type>* root;
struct stNode {
Node<Type>* node;
int timesPop;
stNode(Node<Type>* N = NULL) :node(N), timesPop(0) {}
};
public:
BinaryTree() :root(NULL) {}
BinaryTree(const Type& value) {
root = new Node<Type>(value);
}
~BinaryTree() {
clear();
}
void clear() {
if (root != NULL)
clear(root);
root = NULL;
}
void createTree(Type flag) {
queue<Node<Type>*> que;
Node<Type>* tmp;
Type x, ldata, rdata;
cin >> x;
root = new Node<Type>(x);
que.push(root);
while (!que.empty())
{
tmp = que.front();
que.pop();
cin >> ldata >> rdata;
if (ldata != flag)
que.push(tmp->left = new Node<Type>(ldata));
if (rdata != flag)
que.push(tmp->right = new Node<Type>(rdata));
}
}
// 创建一个树 并设置标志
private:
void clear(Node<Type>* t) {
if (t->left != NULL)
clear(t->left);
if (t->right != NULL)
clear(t->right);
delete t;
return;
}
// 清除结点t
};
template <class Type>
class TreeIterator {
public:
TreeIterator(const BinaryTree<Type>& BT) : T(BT), current(NULL) {}
virtual ~TreeIterator() {}
// 第一个被访问的结点地址送 current
virtual void First() = 0;
// 下一个被访问的结点地址送 current
virtual void operator++() = 0;
// 判当前结点为空吗, 为空返回 True
bool operator+() const { return current != NULL; }
// 返回当前结点指针 current 所指向的结点的数据值。
Type& operator()() const { return current->data; }
public:
const BinaryTree<Type>& T;
// BinaryTree<Type>::Node* current;
Node<Type>* current;
// 指向当前结点的指针。
};
template <class Type>
class Preorder : public TreeIterator<Type> //前序遍历迭代器
{
public:
Preorder(const BinaryTree<Type>& R) : TreeIterator<Type>(R) { s.push(this->T.root); }
~Preorder() {}
void First() {
s.makeEmpty();
if (this->T.root) s.push(this->T.root);
operator++();
}
void operator++() {
if (s.isEmpty()) {
this->current = NULL; return;
}
this->current = s.top();
s.pop();
// 得到当前结点的地址, 并进行出栈操作。
if (this->current->right != NULL)
s.push(this->current->right); //非空右儿子进栈。
if (this->current->left != NULL)
s.push(this->current->left); //非空左儿子进栈。
}
protected:
linkStack<Node<Type>*> s;
};
template <class Type>
class Postorder : public TreeIterator<Type>
{
public:
Postorder(const BinaryTree < Type >& R) : TreeIterator<Type>(R) {
s.push(StNode<Type>(this->T.root));
}
~Postorder() { }
// 后序遍历时的第一个结点的地址。
void First() {
s.makeEmpty();
if (this->T.root) s.push(this->T.root);
operator++();
}
// 后序遍历时的下一个结点的地址。
void operator++() {
if (s.isEmpty())
{ // 当栈空且 current 也为空时, 遍历结束。
this->current = NULL;
return;
} // 置当前指针为空,结束。
StNode<Type> Cnode;
for (; ; )
{
{ Cnode = s.top(); s.pop();
if (++Cnode.TimesPop == 3) //该结点可访问。
{
this->current = Cnode.node; return;
}
s.push(Cnode);
if (Cnode.TimesPop == 1) // 转向访问左子树。
{
if (Cnode.node->left != NULL) // 有左儿子,进栈。
s.push(StNode<Type>(Cnode.node->left));
}
else {// Cnode.TimesPop == 2 访左结束,转向访问右子树。
if (Cnode.node->right != NULL)
s.push(StNode<Type>(Cnode.node->right));
}
}
}
}
protected:
linkStack<StNode<Type>> s;
};
template <class Type> class Inorder : public Postorder < Type >
{
public:
Inorder(const BinaryTree < Type >& R) : Postorder<Type>(R) { }
void operator++(); // 中序时的下一个结点的地址。
};
template <class Type>
void Inorder<Type>::operator++()
{
if (this->s.isEmpty())
{ // 当栈空且 current 也为空时, 遍历结束。
this->current = NULL; return;
} // 置当前指针为空, 结束。
StNode<Type> Cnode;
for (; ; )
{
Cnode = this->s.top(); this->s.pop();
if (++Cnode.TimesPop == 2) // 左子树已处理,该结点可访问。
{
this->current = Cnode.node;
if (Cnode.node->right != NULL) // 有右儿子,进栈。
this->s.push(StNode<Type>(Cnode.node->right));
return;
}
this->s.push(Cnode);
if (Cnode.node->left != NULL)
this->s.push(StNode<Type>(Cnode.node->left));
}
}
int main()
{
BinaryTree<int> tree;
tree.createTree(-1);
Preorder<int> pre(tree);
for (pre.First(); +pre; ++pre)
cout << pre() << ' ';
cout << endl;
Inorder<int> in(tree);
for (in.First(); +in; ++in)
cout << in() << ' ';
cout << endl;
Postorder<int> post(tree);
for (post.First(); +post; ++post)
cout << post() << ' ';
return 0;
}
附录
分类专栏
链接:
本专栏上一节
链接: