最近复习了后续遍历的非递归实现,就算已经看过很多遍了,自己手搓的时候也得想一会。
最开始发明非递归遍历二叉树的人到底是怎么想的啊啊啊啊
琢磨了一段时间,想出了一种以递归方式思考的编码方法,至少对我自己来说比较好理解
但好像有种走歪了的感觉
一、思路
众所周知,最简单的写法是这样的
void postOrderTraversal(TreeNode* root)
{
if(!root) return;
postOderTraversal(root->left);
postOderTraversal(root->right);
visit(root);
}
函数的调用都在栈上进行,调用函数时会压栈,同时也会保存当前函数执行的一些信息(形参之类的),以下代码的行为与真正的函数调用可能有差别,都是笔者自己的理解
二、代码
成员函数:
void postOderTraversal()
{
//简单包装一下,使节点能够储存信息
struct Node
{
TreeNode *tree_node;
int step;
Node(TreeNode *t) : tree_node(t), step(1) {}
operator bool() {return tree_node != nullptr;}//使Node对象能用作判断条件
void go_right() {tree_node = tree_node->right;}//成员指针向右走
void go_left() {tree_node = tree_node->left;}
};
Node root(this->root);
stack<Node> s;
s.push(root);
while (!s.empty())
{
root = s.top(); // root:正在执行的函数的形参
if (!root) //传入了空指针,在递归写法里,函数返回(出栈)
{
s.pop();
continue;
}
//每次都检查函数执行的步骤
if (root.step == 1) //函数准备执行第一步postOderTraversal(root->left);
{
s.top().step = 2; //当前函数的下一步执行第二步
// root将被压栈,将被压栈的是新函数调用,所以要“初始化”形参和执行步骤
root.go_left();
root.step = 1;
s.push(root); //压入新函数
}
else if (root.step == 2) //与上面同理
{
s.top().step = 3;
root.go_right();
root.step = 1;
s.push(root);
}
else if (root.step == 3) //函数准备执行第三步visit(root)
{
cout << root.tree_node->val << " ";
s.pop(); //执行完出栈
}
}
}
完整测试代码:
测试用了二叉搜索树(比较好插入)
#include <iostream>
using std::cout;using std::endl;
#include <stack>
using std::stack;
struct TreeNode
{
int val;
TreeNode *left;
TreeNode *right;
TreeNode() : val(0), left(nullptr), right(nullptr) {}
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
};
class binTree
{
private:
TreeNode *bin_add(TreeNode *root, int val)
{
if (!root)
return new TreeNode(val);
if (val < root->val)
{
root->left = bin_add(root->left, val);
}
else
{
root->right = bin_add(root->right, val);
}
return root;
}
public:
TreeNode *root;
binTree(int val)
{
root = new TreeNode(val);
}
binTree &add(int val)
{
root = bin_add(root, val);
return *this;
}
//需要声明pre变量以记录右节点是否刚刚被访问
//或者创建新类保存执行步骤信息
void postOderTraversal()
{
//简单包装一下,使节点能够储存信息
struct Node
{
TreeNode *tree_node;
int step;
Node(TreeNode *t) : tree_node(t), step(1) {}
operator bool() { return tree_node != nullptr; } //使Node对象能用作判断条件
void go_right() { tree_node = tree_node->right; } //成员指针向右走
void go_left() { tree_node = tree_node->left; }
};
Node root(this->root);
stack<Node> s;
s.push(root);//栈顶是“当前函数”,即递归函数的第一层
while (!s.empty())
{
root = s.top(); // root:正在执行的函数的形参
if (!root) //传入了空指针,在递归写法里,函数返回(出栈)
{
s.pop();
continue;
}
//每次都检查函数执行的步骤
if (root.step == 1) //函数准备执行第一步postOderTraversal(root->left);
{
s.top().step = 2; //下一步执行第二步
// root将被压栈,将被压栈的是新函数调用,所以要“初始化”形参和执行步骤
root.go_left();
root.step = 1;
s.push(root); //压入新函数
}
else if (root.step == 2) //与上面同理
{
s.top().step = 3;
root.go_right();
root.step = 1;
s.push(root);
}
else if (root.step == 3) //函数准备执行第三步visit(root)
{
cout << root.tree_node->val << " ";
s.pop(); //执行完出栈
}
}
}
void postOderTraversal_(TreeNode *r)//递归写法
{
if (!r) return;
postOderTraversal_(r->left);
postOderTraversal_(r->right);
cout << r->val << " ";
}
};
int main(int argc, char const *argv[])
{
/**
* 3
* 1 5
* 0 2 7
* 6
*
*/
binTree bt(3);
bt.add(5).add(1).add(7).add(6).add(0).add(2);
bt.postOderTraversal_(bt.root);//递归写法
cout << endl;
bt.postOderTraversal();//非递归写法
cout << endl;
return 0;
}
输出:
0 2 1 6 7 5 3
0 2 1 6 7 5 3
c++新手,有错误和不规范的地方希望指出,便于学习改进!