二叉树的 前序、中序遍历(非递归)
二叉树基本结构应该都知道,我就不赘述了,其遍历规则倒是经常出现的知识点,根据根节点的相对位置又分为前序遍历,中序遍历,后序遍历。
要是使用递归的方法,直接控制输出的顺序即可
以前序遍历为例
void BiTree::PreOrder(BiNode *bt){
if(bt==nullptr){
return;
}
else{
cout<<bt->data<<endl;
PreOrder(bt->lchild);
PreOrder(bt->rchild);
}
}
这样就行了,但往往是不允许使用递归的,以下就是非递归算法
思路及代码
首先是结构体的基础代码
struct BiNode{
int data;
BiNode* lchild, * rchild;
};
struct BiNode{
int data;
BiNode* lchild, * rchild;
};
class BiTree {
private:
BiNode* root;
public:
BiTree();
BiNode* GetRoot() { return root; }
BiNode* creat();
void PreOrder();
void InOrder();
void PostOrder(BiNode* p);
};
BiNode* BiTree::creat() {
BiNode* bt;
int a;
cout << "请输入节点的值,-1代表此节点不存在" << endl;
cin >> a;
if(a==-1){
bt = nullptr;
}
else {
bt = new BiNode;
bt->data = a;
cout << "请输入" << bt->data<<"的左子树:"<<endl;
bt->lchild = creat();
cout << "请输入" << bt->data << "的右子树:" << endl;
bt->rchild = creat();
}
return bt;
}
BiTree::BiTree() {
root = creat();
}
前序遍历(根左右)
前序遍历的关键在于,访问玩全部的左子树后,如何再找到最初结点的右子树,这就要求我们在输出根节点的值后,保留其地址,以便遍历完左子树后找到右子树地址。这就很容易想到一种存储结构——栈。
指针指向根节点
循环3步:
输出指针指向的结点的数据
该结点存入栈,准备在访问完左子树后用它访问右子树
指针指向该结点的左子树
如果指针指向为空(无左子树了)
就回到上一个结点,指针指向右子树
继续上一个循环
void BiTree::PreOrder(){
BiNode* bt = root;
BiNode* s[10];//栈,用于存储节点地址
int top = -1;//确定操作位置
cout << "前序遍历结果为:" << endl;
while (bt != nullptr || top != -1) {
while (bt != nullptr) {
cout << bt->data;
s[++top] = bt;
bt = bt->lchild;
}
if (top != -1) {
bt = s[top--];//把先前存入的节点出栈
bt = bt->rchild;
}
}
cout << '\n';
}
中序遍历(左根右)
与前序遍历有一点点不同,这个是要先把每个相对的根节点入栈,等出栈的时候再输出值,所以只要改动一下输出位置即可。
void BiTree::InOrder() {
BiNode* bt = root;
BiNode* s[10];//栈,用于存储节点地址
int top = -1;//确定操作位置
cout << "中序遍历结果为:" << endl;
while (bt != nullptr || top != -1) {
while (bt != nullptr) {
s[++top] = bt;
bt = bt->lchild;
}
if (top != -1) {
bt = s[top--];//把先前存入的节点出栈
cout << bt->data;
bt = bt->rchild;
}
}
cout << '\n';
}
后序遍历(左右根)
这个遍历如果不递归将会异常繁琐,所以我这里就不展开讲了(其实是我改bug越改越多)直接递归解决问题吧
void BiTree::PostOrder(BiNode *p) {
if (p == nullptr) { return; }
else {
PostOrder(p->lchild);
PostOrder(p->rchild);
cout << p->data;
}
}
就以这张图来测试
结果符合预期