树的结构定义
每一个树节点包括自己存储的数据和左右孩子的地址。
struct treenode {
int data; //默认存储int类型,需要更改模板类
treenode* left;
treenode* right;
treenode() :data(0), left(nullptr), right(nullptr) {}
treenode(int data) :data(data), left(nullptr), right(nullptr) {}
};
class tree
{
public:
int end;//自定义的叶子节点的左右孩子结束值,简化输入
treenode* p;
tree(int data) :p(new treenode()), end(data) {}
tree():p() {}
void creat(treenode*& p);//创建一颗树
void preorder(treenode* p);
void postorder_stack(treenode* p, std::stack<treenode*>s);
void preorder_stack(treenode* p, std::stack<treenode*>s);
};
使用递归方法创建树
void tree::creat(treenode*& p) {
int data;
std::cin >> data;
if (data == this->end) {
delete p;
p = nullptr;
}
else {
p = new treenode(data);
creat(p->left);
creat(p->right);
}
}
递归遍历
使用递归方法遍历二叉树,输出节点,只在于输出节点的位置
//递归方式遍历
void tree::preorder(treenode* p) {
if (p == nullptr)
std::cout;
else {
//std::cout << p->data 写在此处为先序, 即每次读取一个节点就访问这个节点
preorder(p->left);
//std::cout << p->data 写在此处为中序, 即每次左子树访问完成访问节点
preorder(p->right);
std::cout << p->data << " ";//此处为后序, 每次右子树访问完全后访问节点
}
}
使用栈实现遍历
使用栈的方式遍历,理论上所有递归函数都可以用栈方式重写,因为递归实质就是函数的不断进出栈过程。
栈实现先序和中序比较简单,因为访问方式符合栈的进出方式,左子树不断进栈,无左子树,当前出栈,得到父亲,再遍历
右子树。
先序和中序
void tree::preorder_stack(treenode* p, std::stack<treenode*> s)
{
treenode* p1 = p;
while (p1 != nullptr || !s.empty()) {
if (p1 != nullptr) {
s.push(p1);
p1 = p1->left;//遍历左子树,如果有当前节点存在,就压进栈
//std::cout << p1->right;
}
else {
p1 = s.top();//左子树为空,回到当前栈顶节点,也就是不存在节点的父亲,准备遍历右子树
s.pop();//当前节点出栈
std::cout << p1->data << " ";//访问当前节点
p1 = p1->right;//遍历右子树
}
}
std::cout << std::endl;
}
进行栈实现的后续遍历会复杂很多,我们需要使用一个零时的节点去记录是否访问过每个节点左右孩子,如果访问过了
接需要标记出来,我使用的是如果左孩子被访问了,就将左孩子干掉,如果右孩子被访问了,就把右孩子干掉,
但更好的方法是创建一个记录值,访问过左孩子,数值+1,数值为1只能访问右孩子,数值再+1,为2则不能访问。
后续遍历(较难)
void tree::postorder_stack(treenode* p, std::stack<treenode*>s)
{
treenode* p1 = p;
treenode* temp;
s.push(p1);
while (!s.empty()) {
if (p1->left != nullptr) {//如果左子树不为空,遍历左子树
s.push(p1->left);//不为空就压栈
temp = p1;//temp记录当前节点
p1 = p1->left;//p1指向下一节点
temp->left = nullptr;//temp记录节点左孩子为空
}
else {
if (p1->right != nullptr) {//每一次没有左孩子就来遍历右孩子
temp = p1;//记录当前节点
p1 = p1->right;//p1指向下一节点
temp->right = nullptr;//temp记录节点右孩子为空,标记为已访问过
s.push(p1);//p1压入栈
}
else {//如果没左孩子也没右孩子了,就输出当前节点
std::cout << p1->data << " ";
s.pop();//完成当前节点访问,出栈
if(!s.empty())
p1 = s.top();//如果栈不为空,获取栈顶元素
}
}
}
std::cout << std::endl;
}
栈实现的后续遍历是我根据递归时出入栈的方式模拟过程实现的,建议大家在学习的时候多根据原理反推实现,会有
很好的效果!
PS:2020.11.25
BY:Aaa2