一 定义
二叉树非递归遍历采用的是栈结构进行管理输出方式,采用循环去替代递归。和层次遍历有相似之处。
二 结构
data | left | right | flag |
flag用于后顺遍历时标记右侧树的访问状态,若左右子树都被访问后(为null或者flag==1),则可以弹出当前节点并打印.
三 源码
#include <iostream>
#include <stack>
using namespace std;
struct TreeNode
{
int data;
TreeNode* leftNode;
TreeNode* rightNode;
int flag;
};
TreeNode* creatTree(int arr[], int size, int* index) {
// 空数组 || 已经遍历到最后
if (!arr || *index >= size) {
return NULL;
}
int value = arr[*index];
(*index)++;
// 值为 -1 时认为是空节点
if (value == -1) {
return NULL;
}
TreeNode* tree = new TreeNode();
tree->data = value;
tree->flag = 0;
tree->leftNode = creatTree(arr, size, index);
tree->rightNode = creatTree(arr, size, index);
return tree;
}
/// 递归前序遍历
void prePrint(TreeNode* root) {
if (root) {
printf("%d ", root->data);
prePrint(root->leftNode);
prePrint(root->rightNode);
}
}
/// 非递归前序遍历
/// 其核心逻辑是先将当前树节点的左子树压栈,当左子树为空时访问当前节点的右子树
void prePrintUseStack(TreeNode* root) {
std::stack<TreeNode*> tempStack;
while (root || !tempStack.empty()) {
if (root) {
printf("node:%d \n", root->data); // 根
tempStack.push(root);
root = root->leftNode; // 左
}
else {
root = tempStack.top()->rightNode; // 右
// 当节点已经访问了左子树和右子树就可以被弹出抛弃了
tempStack.pop();
}
}
}
/// 非递归中序
void middlePrint(TreeNode* root) {
std::stack<TreeNode*> tempStack;
while (root || !tempStack.empty()) {
// 将左侧节点全部压入
if (root) {
tempStack.push(root);
root = root->leftNode; // 左
}
else {
root = tempStack.top(); // 根
//if (root) {
printf("%d ", root->data);
root = root->rightNode; // 右
//}
tempStack.pop();
}
}
}
void backOrder(TreeNode* root) {
std::stack<TreeNode*> tempStack;
while (root || !tempStack.empty()) {
if (root) {
tempStack.push(root);
root = root->leftNode; // 左
}
else {
TreeNode* tempNode = tempStack.top(); // 根
if (tempNode) {
if (tempNode->rightNode && tempNode->rightNode->flag == 0) {
root = tempNode->rightNode;
}
else { // 右节点为空或者已经被访问,那就访问当前根节点
printf("%d ", tempNode->data);
tempNode->flag = 1; // 设置当前节点已经访问
tempStack.pop();
}
}
}
}
}
int main() {
int arr[] = { 1,2,4,-1,-1,5,-1,-1,3,6,-1,-1,7,-1,-1 };
int size = sizeof(arr) / sizeof(int);
int index = 0;
TreeNode* tree = creatTree(arr, size, &index);
//prePrint(tree);
//prePrintUseStack(tree);
//middlePrint(tree);
backOrder(tree);
}
四 总结
1 前序打印是入栈时就进行打印,中序遍历和后序遍历在出栈时进行打印。
2 后序遍历需要增加tag来标定右子树访问状态
3 所有打印形式都是左子树先全部压栈,然后再处理左子树、根子树和右子树之间的关系,左子树的压栈顺序是主要线索。
4 最后发现,还是根据左子树与右子树之间的关系来决定当前节点是否可以打印