10.4-5 给定一个n结点的二叉树,写出一个 O ( n ) O(n) O(n)时间的非递归过程,将该树的每一个结点的关键字输出。要求除该树本身的存储空间外只能使用固定量的额外存储空间,且中过程中不得修改该树,即使是暂时的修改也不允许。
要完成 O ( 1 ) O(1) O(1)的空间内遍历该树,需要每个结点需要能访问其父节点进行回溯。
struct Tree
{
Tree *parent;
Tree *left;
Tree *right;
int key;
};
中序遍历:
- 访问左孩子
- 访问右孩子
- 从左孩子返回,访问根节点,进入右孩子访问
- 从右孩子返回,整棵树访问完成,回溯到上层,直到当前结点为根节点或该结点是父节点的左孩子
下面是测试代码:
#include<iostream>
#include<stack>
using namespace std;
#define NIL -1
struct Tree
{
Tree* left;
Tree* right;
Tree* parent;
int key;
Tree():left(nullptr),right(nullptr),parent(nullptr){}
};
void print(const Tree* t)
{
cout << t->key << " ";
}
void createTree(Tree *&t)
{
int key;
cin >> key;
if(key==NIL){
t=nullptr;
return;
}
t=new Tree();
t->key=key;
createTree(t->left);
createTree(t->right);
//存在子结点,更细父指针
if(t->left)
t->left->parent=t;
if(t->right)
t->right->parent=t;
}
// 递归版本
void inorder_travel(Tree *h)
{
if(h)
{
inorder_travel(h->left);
print(h);
inorder_travel(h->right);
}
}
// 使用stack非递归版本,结点不需要parent指针
void inorder_byStack(Tree*h)
{
stack<Tree*> sta;
Tree *p=h;
while(p || !sta.empty())
{
while(p)
{
sta.push(p);
p=p->left;
}
p=sta.top();sta.pop();
print(p);
p=p->right;
}
}
// 没有使用栈的版本,通过h和pre之间的关系进行遍历。
// 回溯需要parent指针。
void inorder_noStack(Tree* h)
{
if(!h)return;
//pre作为前驱,判断从左孩子还是右孩子返回
Tree* pre=nullptr;
while(true)
{
//找到左结点
if(pre!=h->left)
{
while(h->left)
h=h->left;
}
print(h);
//存在右结点则进入
if(h->right){
h=h->right;
continue;
}
//右孩子返回时,回溯直到根结点的父节点或是父节点的左孩子
do{
pre=h;
h=h->parent;
if(h==nullptr)
return;
}while(pre==h->right);
}
}
int main()
{
Tree* head;
createTree(head);
inorder_travel(head);
cout << endl;
inorder_byStack(head);
cout << endl;
inorder_noStack(head);
}
测试数据:
1 2 4 7 -1 -1 -1 5 8 -1 -1 -1 3 6 -1 9 -1 -1 -1
树的形状是:
中序遍历结果:
7 4 2 8 5 1 6 9 3