【代码随想录】【算法训练营】【第14天】 [144]二叉树的前序遍历 [94]二叉树的中序遍历 [145]二叉树的后序遍历

前言

思路及算法思维,指路 代码随想录
题目来自 LeetCode

day 14,周二,好困~

题目详情

[144] 二叉树的前序遍历

题目描述

144 二叉树的前序遍历
144 二叉树的前序遍历

解题思路

前提:二叉树的前序遍历,中左右
思路:递归实现 or 迭代实现。
重点:递归实现三要素:确定递归函数的参数和返回值,确定终止条件,确定单层递归逻辑。

代码实现

C语言
递归法

确定递归函数的参数和返回值:void traversal(struct TreeNode *root, int *ret, int *idx);
确定终止条件:if (root == NULL) { return ; };
确定单层递归逻辑:ret[(*idx)++] = root->val;// 中 traversal(root->left, ret, idx);// 左 traversal(root->right, ret, idx);// 右。

/**
 * 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 traversal(struct TreeNode *root, int *ret, int *idx)
{
    if (root == NULL)
    {
        return;
    }
    ret[(*idx)++] = root->val;    // 中
    traversal(root->left, ret, idx);    // 左
    traversal(root->right, ret, idx);    // 右
    return ;
}

int* preorderTraversal(struct TreeNode* root, int* returnSize) {
    int *ret = (int *)malloc(sizeof(int) * 100);
    *returnSize = 0;
    traversal(root, ret, returnSize);
    return ret;   
}

递归的实现:每一次递归调用都会把函数的局部变量、参数值、返回地址压入调用栈中;递归返回时,从栈顶弹出上一次递归的各项参数。

迭代法

迭代的实现,就是递归栈的手动调用实现。
需要注意的是,栈的“先进后出”特征,前序遍历,需要将左节点晚于右节点入栈。

/**
 * 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().
 */
int* preorderTraversal(struct TreeNode* root, int* returnSize) {
    int *ret = (int *)malloc(sizeof(int) * 100);
    memset(ret, 0, sizeof(int) * 100);
    *returnSize = 0;

    // 栈
    struct TreeNode *stack[100];
    memset(stack, 0, sizeof(struct TreeNode *) * 100);
    int idx = 0;
    // 将根节点压栈
    stack[idx++] = root;
    while (idx > 0)
    {
        struct TreeNode *cur = stack[--idx];
        // 判空
        if (cur != NULL)
        {
            // 前序遍历,保存结点值
            ret[(*returnSize)++] = cur->val;
            // right压栈,空节点不入栈
            if (cur->right != NULL)
            {
                stack[idx++] = cur->right;
            }
            // left压栈,空节点不入栈
            if (cur->left != NULL)
            {
                stack[idx++] = cur->left;
            }
        }
    }

    return ret;   
}
统一迭代法

了解前序、中序、后序遍历的迭代法实现之后,会发现,3种遍历的迭代实现,除了前序和后序的实现有点关联,中序遍历的迭代实现,稍微有些不一样。
针对这种情况,在栈中,将要处理的节点(子树的根节点)放入栈中后,紧接着放入一个null指针作为标记,这样,每次遍历到null指针的时候,就知道下一个从栈中弹出的元素,就是要输出的元素。

/**
 * 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().
 */
int* preorderTraversal(struct TreeNode* root, int* returnSize) {
    int *ret = (int *)malloc(sizeof(int) * 100);
    memset(ret, 0, sizeof(int) * 100);
    *returnSize = 0;

    // 栈
    struct TreeNode *stack[100];
    memset(stack, 0, sizeof(struct TreeNode *) * 100);
    int idx = 0;
    // 判断是否为空树
    if (root != NULL)
    {
        stack[idx++] = root;
    }
    // 非空树时进入循环
    while (idx > 0)
    {
        // 取栈顶元素
        struct TreeNode *cur = stack[--idx];
        // 当前结点不为null指针时, 由于前序遍历,依次对 右节点、左节点、根节点、null指针进行入栈
        if (cur != NULL)
        {
            // right结点,空节点不入栈
            if (cur->right != NULL)
            {
                stack[idx++] = cur->right;
            }
            // left结点,空节点不入栈
            if (cur->left != NULL)
            {
                stack[idx++] = cur->left;
            }
            // 该结点压栈,并放入空指针标识已遍历的结点
            stack[idx++] = cur;
            stack[idx++] = NULL;
        }
        // 当前结点为null指针时,说明栈中下一个节点需要保存输出
        else
        {
            // 找到标记点,直接打印后一结点的val
            ret[(*returnSize)++] = (stack[--idx])->val;
        }
    }

    return ret;   
}

[94] 二叉树的中序遍历

题目描述

94 二叉树的中序遍历
94 二叉树的中序遍历

解题思路

前提:二叉树的中序遍历, 左中右
思路:递归实现 or 迭代实现
重点:递归实现三要素:确定递归函数的参数和返回值,确定终止条件,确定单层递归逻辑。

代码实现

C语言
递归法

确定递归函数的参数和返回值:void traversal(struct TreeNode *root, int *ret, int *returnSize);
确定终止条件:if (root == NULL) { return ; };
确定单层递归逻辑:traversal(root->left, ret, returnSize);//左 ret[(*returnSize)++] = root->val;//中 traversal(root->right, ret, returnSize);//右。

/**
 * 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 traversal(struct TreeNode *root, int *ret, int *returnSize)
{
    if (root == NULL)
    {
        return ;
    }
    traversal(root->left, ret, returnSize);    // 左
    ret[(*returnSize)++] = root->val;     // 中
    traversal(root->right, ret, returnSize);    // 右
    return ;
}

int* inorderTraversal(struct TreeNode* root, int* returnSize) {
    int *ret = (int *)malloc(sizeof(int) * 100);
    *returnSize = 0;
    traversal(root, ret, returnSize);
    return ret;
}
迭代法

中序遍历的迭代方法,与前序、后序的实现不太一样,各子树的根节点在左节点和右节点之间输出,需要遍历左子树到叶子结点,再依次输出叶子结点与根节点(入栈顺序为根节点、左节点),然后遍历右子树。

/**
 * 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().
 */

int* inorderTraversal(struct TreeNode* root, int* returnSize) {
    int *ret = (int *)malloc(sizeof(int) * 100);
    *returnSize = 0;
    int idx = 0;
    struct TreeNode *stack[100];
    struct TreeNode *cur = root;
    // 判断遍历是否完成: 栈为非空 或 当前结点为指向null结点,即为遍历未完成
    while ((idx > 0) || (cur != NULL))
    {
        // 当前结点存在,则入栈,继续遍历其左子树
        if (cur != NULL)
        {
            stack[idx++] = cur;    // 中
            cur = cur->left;    // 左
        }
        // 当前结点为null,说明左子树已经遍历完成,输出栈顶的根节点,并遍历其右子树
        else
        {
            ret[(*returnSize)++] = (stack[--idx])->val;    // 输出节点数值
            cur = stack[idx]->right;    // 右
        }
    }

    return ret;
}
统一迭代法

将要处理的节点(子树的根节点)放入栈中后,紧接着放入一个null指针作为标记。

/**
 * 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().
 */

int* inorderTraversal(struct TreeNode* root, int* returnSize) {
    int *ret = (int *)malloc(sizeof(int) * 100);
    *returnSize = 0;
    int idx = 0;
    struct TreeNode *stack[100];
    // 判断空树
    if (root != NULL)
    {
        stack[idx++] = root;
    }
    // 非空树
    while (idx > 0)
    {
        // 取栈顶节点
        struct TreeNode *cur = stack[--idx];
        // 当前结点不为null指针时, 由于中序遍历,依次对 右节点、根节点、null指针、左节点进行入栈
        if (cur != NULL)
        {
            // right,空节点不入栈
            if (cur->right)
            {
                stack[idx++] = cur->right;
            }
            // 压栈当前结点,并放入空指针标识已遍历的结点
            stack[idx++] = cur;
            stack[idx++] = NULL;
            // left,空节点不入栈
            if (cur->left)
            {
                stack[idx++] = cur->left;
            }
        }
        // 当前结点为null指针时,说明栈中下一个节点需要保存输出
        else
        {
            // 取栈顶的数值
            ret[(*returnSize)++] = (stack[--idx])->val;
        }
    }

    return ret;
}

[145] 二叉树的后序遍历

题目描述

145 二叉树的后序遍历
145 二叉树的后序遍历

解题思路

前提:二叉树的后序遍历 左右中
思路:递归实现 or 迭代实现。
重点:递归实现三要素:确定递归函数的参数和返回值,确定终止条件,确定单层递归逻辑。

代码实现

C语言
递归法

确定递归函数的参数和返回值:void traversal(struct TreeNode *root, int *ret, int *returnSize);
确定终止条件:if (root == NULL) { return ; };
确定单层递归逻辑:traversal(root->left, ret, returnSize);//左 traversal(root->right, ret, returnSize);//右 ret[(*returnSize)++] = root->val;//中。

/**
 * 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 traversal(struct TreeNode *root, int *ret, int *returnSize)
{
    if (root == NULL)
    {
        return ;
    }
    traversal(root->left, ret, returnSize);    // 左
    traversal(root->right, ret, returnSize);     // 右
    ret[(*returnSize)++] = root->val;     // 中
    return ;
}

int* postorderTraversal(struct TreeNode* root, int* returnSize) {
    int *ret = (int *)malloc(sizeof(int) * 100);
    *returnSize = 0;
    traversal(root, ret, returnSize);
    return ret;
}
迭代法

由于后序遍历需要将根节点最后输出,迭代较难实现遍历完左右子树后输出根节点,较容易实现根节点优先输出,所以,考虑最后反转数组,使后序遍历转换为“中右左”的遍历方式。此时,根据栈的“先进后出”特征,需要将右节点晚于左节点入栈。

/**
 * 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().
 */

int* postorderTraversal(struct TreeNode* root, int* returnSize) {
    int *ret = (int *)malloc(sizeof(int) * 100);
    *returnSize = 0;
    
    struct TreeNode *stack[100];
    int idx = 0;
    stack[idx++] = root;
    while (idx > 0)
    {
        struct TreeNode *cur = stack[--idx];
        // 判断是否为空树
        if (cur != NULL)
        {
            // 结点值
            ret[(*returnSize)++] = cur->val;
            // left,空节点不入栈
            if (cur->left)
            {
                stack[idx++] = cur->left;
            }
            // right,空节点不入栈
            if (cur->right)
            {
                stack[idx++] = cur->right;
            }
        }
    }
    // 反转结果数组
    for (int i = 0; i < (*returnSize / 2); i++)
    {
        int tmp = ret[i];
        ret[i] = ret[*returnSize - 1 - i];
        ret[*returnSize - 1 - i] = tmp;
    }

    return ret;
}
统一迭代法

将要处理的节点(子树的根节点)放入栈中后,紧接着放入一个null指针作为标记。

/**
 * 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().
 */

int* postorderTraversal(struct TreeNode* root, int* returnSize) {
    int *ret = (int *)malloc(sizeof(int) * 100);
    *returnSize = 0;
    
    struct TreeNode *stack[100];
    int idx = 0;
    // 判断空树
    if (root != NULL)
    {
        stack[idx++] = root;
    }
    // 不为空树
    while (idx > 0)
    {
        struct TreeNode *cur = stack[--idx];
        // 当前结点不为null指针时, 由于后序遍历,依次对 根节点、null指针、右节点、左节点进行入栈
        if (cur != NULL)
        {
            // 根节点入栈,并放入空指针标识已遍历的结点
            stack[idx++] = cur;
            stack[idx++] = NULL;            
            // right,空节点不入栈
            if (cur->right)
            {
                stack[idx++] = cur->right;
            }
            // left,空节点不入栈
            if (cur->left)
            {
                stack[idx++] = cur->left;
            }
        }
        // 当前结点为null指针时,说明栈中下一个节点需要保存输出
        else
        {
            ret[(*returnSize)++] = (stack[--idx])->val;
        }
    }

    return ret;
}

今日收获

  1. 二叉树的前序、中序、后序遍历,分别使用 递归法、迭代法、统一风格的迭代法 实现。
  2. 递归实现三要素:确定递归函数的参数和返回值,确定终止条件,确定单层递归逻辑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值