前序遍历——题目描述:
给你二叉树的根节点 root
,返回它节点值的 前序 遍历。
示例 1:
输入:root = [1,null,2,3] 输出:[1,2,3]
示例 2:
输入:root = [] 输出:[]
示例 3:
输入:root = [1] 输出:[1]
示例 4:
输入:root = [1,2] 输出:[1,2]
示例 5:
输入:root = [1,null,2] 输出:[1,2]
提示:
- 树中节点数目在范围
[0, 100]
内 -100 <= Node.val <= 100
解题方法:
1.递归法
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
//递归算法
//由于需要连续传参数组,故必须另写一个函数
void preOrder(struct TreeNode* node,int* returnSize,int* ans)
{
if(node==NULL)
return;
else
ans[(*returnSize)++]=node->val;
preOrder(node->left,returnSize,ans);
preOrder(node->right,returnSize,ans);
}
int* preorderTraversal(struct TreeNode* root, int* returnSize)
{
int* ans=(int*)malloc(sizeof(int)*101);
*returnSize=0; //returnSize必须赋值作为数组指针
preOrder(root,returnSize,ans);
return ans;
}
2.迭代法
//迭代算法
int* preorderTraversal(struct TreeNode* root, int* returnSize)
{
//定义返回数组
int* ans=(int*)malloc(sizeof(int)*101);
*returnSize=0;
//定义栈结构
struct TreeNode* stack[101];
int top=0;
while(top!=0 || root!=NULL) //注意此处是||而非&&
{
//将当前结点的所有左子树入栈
while(root!=NULL)
{
ans[(*returnSize)++]=root->val;
stack[top++]=root;
root=root->left;
}
//出栈
root=stack[--top];//由于上次++后top指向空,故此时应--
//访问右孩子
root=root->right;
}
return ans;
}
中序遍历——题目描述:
给定一个二叉树的根节点 root
,返回 它的 中序 遍历 。
示例 1:
输入:root = [1,null,2,3] 输出:[1,3,2]
示例 2:
输入:root = [] 输出:[]
示例 3:
输入:root = [1] 输出:[1]
提示:
- 树中节点数目在范围
[0, 100]
内 -100 <= Node.val <= 100
解题思路:中序遍历与先序遍历的代码和解题思路十分相似,仅需要修改一下代码顺序,此处不过多赘述。
后序遍历——题目描述:
给你一棵二叉树的根节点 root
,返回其节点值的 后序遍历 。
示例 1:
输入:root = [1,null,2,3] 输出:[3,2,1]
示例 2:
输入:root = [] 输出:[]
示例 3:
输入:root = [1] 输出:[1]
提示:
- 树中节点的数目在范围
[0, 100]
内 -100 <= Node.val <= 100
给你一棵二叉树的根节点 root
,返回其节点值的 后序遍历 。
示例 1:
输入:root = [1,null,2,3] 输出:[3,2,1]
示例 2:
输入:root = [] 输出:[]
示例 3:
输入:root = [1] 输出:[1]
提示:
- 树中节点的数目在范围
[0, 100]
内 -100 <= Node.val <= 100
解题思路:
方法一:递归法:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
//递归算法
void postOrder(struct TreeNode* root,int* ans,int* returnSize)
{
if(!root)
return;
else
{
postOrder(root->left,ans,returnSize);
postOrder(root->right,ans,returnSize);
ans[(*returnSize)++]=root->val;
}
}
int* postorderTraversal(struct TreeNode* root, int* returnSize) {
int* ans=(int*)malloc(sizeof(int)*101);
*returnSize=0;
postOrder(root,ans,returnSize);
return ans;
}
后序遍历的递归算法与先序和中序遍历的递归算法十分相似,区别仅在修改代码顺序。
方法二:迭代法:
// 迭代算法
//后序遍历
//定义一个TreeNode*变量prev,搜索到右节点时,
//把右节点赋给prev,之后出栈返回到根节点,如果根节点->==prev,那么说明右节点已经遍历过
int *postorderTraversal(struct TreeNode *root, int *returnSize)
{
*returnSize = 0;
int *res = (int *)malloc(sizeof(int) * 110);
if (root == NULL)
return res;
struct TreeNode *cur = root; //使用cur遍历当前节点
struct TreeNode *stack[110];
struct TreeNode *prev = NULL; //使用prev记录已经遍历过的右节点
int top = 0;
while (cur || top > 0)
{
while (cur) //将当前节点最左边的子树入栈
{
stack[top++] = cur;
cur = cur->left;
}
cur = stack[--top];
//迭代法后序遍历与先序\中序最大的不同
if (cur->right == NULL || cur->right == prev) //若 当前节点没有右子树 或 右子树已经访问过
{
res[(*returnSize)++] = cur->val;
prev = cur; //记录该节点以及被访问过,只要有节点入res,就将其更新未prev
cur = NULL;
}
else //否则访问右子树
{
stack[top++] = cur;
cur = cur->right;
}
}
return res;
}
非递归后序遍历,即迭代法,与先序和中序的迭代法解题思路不同。关键在于区分当前节点cur是否有右子树或节点的右子树是否已经被记录过(cur->right==prev),若没有右子树或右子树已经被访问,则使用prev记录该结点。这样,只要上一个记录过的结点都使用prev记录起来,这样就天然的完成了对已经记录的右子树标记。(由于后序遍历的特性:左→右→根,所以当前根若具有右子树,则一定是上一个被记录的节点)