本篇文章适合已经了解非递归前中后序遍历逻辑,且想寻找一个适用于三种遍历代码模板的小猿。 本文着重介绍一个模板对三种遍历的套用,对于遍历逻辑讲解粗略。
对于二叉树的非递归遍历要比递归遍历复杂许多,但在企业和考研中却深受喜爱。那有没有一套模板可以同时掌握三种非递归遍历呢?其实非递归的前序和中序遍历比较相似,也比较简单,但后序遍历却大相径庭,尤其是在C语言实现后序遍历时,需要对指针进行反复操作,非常复杂。那么今天介绍一种方法,在掌握前序和中序遍历模板的同时,也掌握复杂的后序遍历。话不多说,直接上代码:
前序遍历
//计算树的结点数,目的是为保存遍历结果的数组匹配一个精确大小,该段代码可有可无
int TreeSize(struct TreeNode* root)
{
return root == NULL ? 0 : TreeSize(root->left) + TreeSize(root->right) + 1;
}
int* preorderTraversal(struct TreeNode* root, int* returnSize)
{
int i = 0;
int size = TreeSize(root);
*returnSize = size;
int *a;
struct TreeNode*stack[100];
int top = 0;
a = (int *)malloc(sizeof(int)*size);
if (root == NULL)
{
return NULL;
}
do
{
while (root)
{
a[i++] = root->val;// 标记处####
stack[top++] = root;
root = root->left;
}
if (top != 0)
{
root = stack[--top];
root = root->right;
}
} while (top != 0 || root != NULL);
return a;
}
中序遍历
//计算树的结点数,目的是为保存遍历结果的数组匹配一个精确大小,该段代码可有可无
int TreeSize(struct TreeNode* root)
{
return root == NULL ? 0 : TreeSize(root->left) + TreeSize(root->right) + 1;
}
int* inorderTraversal(struct TreeNode* root, int* returnSize)
{
int size = TreeSize(root);
*returnSize = size;
struct TreeNode* stack[100];
int *a;
int i = 0;
a = (int *)malloc(sizeof(int)*size);
int top = 0;
if (root == NULL)
{
return NULL;
}
do
{
while (root != NULL)
{
stack[top++] = root;
root = root->left;
}
if (top != 0)
{
root = stack[--top];
a[i++] = root->val;// 标记处###
root = root->right;
}
} while (root != NULL || top != 0);
return a;
}
可以看出,前序遍历和中序遍历中只有标记处的代码做了顺序上的调整,其实也很好理解,前序遍历是在压栈时访问节点,而中序遍历是在出栈时访问节点。
后序遍历:
//计算树的结点数,目的是为保存遍历结果的数组匹配一个精确大小,该段代码可有可无
int TreeSize(struct TreeNode* root)
{
return root == NULL ? 0 : TreeSize(root->left) + TreeSize(root->right) + 1;
}
int* postorderTraversal(struct TreeNode* root, int* returnSize)
{
int i = 0;
int size = TreeSize(root);
*returnSize = size;
int *a;
struct TreeNode*stack[100];
int top = 0;
a = (int *)malloc(sizeof(int)*size);
if (root == NULL)
{
return NULL;
}
do
{
while (root)
{
a[i] = root->val;
i++;
stack[top++] = root;
root = root->right;// 交换①
}
if (top != 0)
{
root = stack[--top];
root = root->left;// 交换②
}
} while (top != 0 || root != NULL);
for (int j = 0; j < i / 2; j++)// 结果转置操作
{
int temp = a[j];
a[j] = a[i - j - 1];
a[i - j - 1] = temp;
}
return a;
}
后序遍历是在前序遍历的基础上更改得到的;前序:根左右 后序:左右根;
如果我们在前序遍历时交换左右节点的入栈顺序,前序遍历就从 根左右——>根右左,如果再转置一下,就从根右左——>左右根,也就变成了后序。
这就是将前序遍历代码中的两个入栈顺序颠倒,最后再转置一下结果即可。
总结:①前序遍历调整访问顺序得到中序遍历
②前序遍历调整入栈顺序,最后逆置得到后序遍历
也就是,只需记住前序遍历就可以掌握三种遍历顺序。