代码随想录算法训练营第十四天| 513. 找树左下角的值、112.路径总和、113.路径总和II、106.从中序与后序遍历序列构造二叉树

513. 找树左下角的值

题目链接:513. 找树左下角的值 - 力扣(LeetCode)

题目描述:给定一个二叉树的 根节点 root,请找出该二叉树的 最底层 最左边 节点的值。

假设二叉树中至少有一个节点。

示例 1:

img

输入: root = [2,1,3]
输出: 1

示例 2:

img

输入: [1,2,3,4,null,5,6,null,null,7]
输出: 7

提示:

  • 二叉树的节点个数的范围是 [1,104]
  • -231 <= Node.val <= 231 - 1

解法一、递归

一直向左遍历即可,但是要注意:一直向左遍历的最后一个节点,未必在最后一行

如何判断是否是最后一行? 深度最大的叶子节点一定是最后一行

如何找最左边的节点? 可以使用前序遍历(中序后序也可),只有保证优先左边搜索,然后记录深度最大的叶子节点,此时就是树的最后一行最左边的值

int maxDepth = -1;
int leftval = 0;

void dfs(struct TreeNode* root, int depth) {
    if (root == NULL)
        return ;
    if (depth > maxDepth) {
        maxDepth = depth;// 更新最大深度
        leftval = root->val;// 最大深度最左面的数值
    }
    dfs(root->left, depth + 1);// 隐藏着回溯
    dfs(root->right, depth + 1);
}

int findBottomLeftValue(struct TreeNode* root){
    maxDepth = -1;
    leftval = 0;
    dfs(root, 1);
    return leftval;
}

解法二、迭代

采用层序遍历的方式,一般每层是从左至右遍历,这样最后一层第一个就是最左边的节点。这里采用从右至左的顺序,这样层序遍历的最后一个节点就是最底层最左边的节点

#define MAX_NODE_SIZE 10000
int findBottomLeftValue(struct TreeNode *root) {
    int ret = 0;
    struct TreeNode *queue[MAX_NODE_SIZE];
    int rear = 0, front = 0;
    queue[rear++] = root;
    while(front != rear){
        int size = rear - front;
        //从右至左遍历每一层
        for(int i=0;i<size;i++){
            struct TreeNode *node = queue[front++];
            if(node->right) queue[rear++] = node->right;
            if(node->left) queue[rear++] = node->left;
            ret = node->val;//每层最后一个节点就是该层最左边的节点
        }    
    }
    return ret;
}

112.路径总和

题目链接:112. 路径总和 - 力扣(LeetCode)

题目描述:给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。如果存在,返回 true ;否则,返回 false

叶子节点 是指没有子节点的节点。

示例 1:

img

输入:root = [5,4,8,11,null,13,4,7,2,null,null,null,1], targetSum = 22
输出:true
解释:等于目标和的根节点到叶节点路径如上图所示。

示例 2:

img

输入:root = [1,2,3], targetSum = 5
输出:false
解释:树中存在两条根节点到叶子节点的路径:
(1 --> 2): 和为 3
(1 --> 3): 和为 4
不存在 sum = 5 的根节点到叶子节点的路径。

示例 3:

输入:root = [], targetSum = 0
输出:false
解释:由于树是空的,所以不存在根节点到叶子节点的路径。

提示:

  • 树中节点的数目在范围 [0, 5000]
  • -1000 <= Node.val <= 1000
  • -1000 <= targetSum <= 1000

解法一、递归

如果累加判断的话,会比较麻烦,因此这里采用递减,让计数器count初始为目标和,然后每次减去遍历路径节点上的数值

//递减判断
bool hasPathSum(struct TreeNode* root, int targetSum){
    // 递归结束条件:若当前节点不存在,返回false
    if(!root)
        return false;
    // 若当前节点为叶子节点,且targetSum-root的值为0。(当前路径上的节点值的和满足条件)返回true
    if(!root->right && !root->left && targetSum == root->val)
        return true;

    // 查看左子树和右子树的所有节点是否满足条件
    return hasPathSum(root->right, targetSum - root->val) || hasPathSum(root->left, targetSum - root->val);
}

//累加判断
bool dfs(struct TreeNode* root, int totalSum, int targetSum) {
    if (root == NULL) {
        return false;
    }
    totalSum += root->val;
    if (root->left == NULL && root->right == NULL && totalSum == targetSum) {
        return true;
    }
    bool left = dfs(root->left, totalSum, targetSum);
    bool right = dfs(root->right, totalSum, targetSum);
    return left || right;
}
bool hasPathSum(struct TreeNode* root, int targetSum) {
    return dfs(root, 0, targetSum);
}

解法二、迭代

// 存储一个节点以及当前的和
struct Pair {
    struct TreeNode* node;
    int sum;
};

bool hasPathSum(struct TreeNode* root, int targetSum){
    struct Pair stack[1000];
    int stackTop = 0;

    // 若root存在,则将节点和值封装成一个pair入栈
    if(root) {
        struct Pair newPair = {root, root->val};
        stack[stackTop++] = newPair;
    }

    // 当栈不为空时
    while(stackTop) {
        // 出栈栈顶元素
        struct Pair topPair = stack[--stackTop];
        // 若栈顶元素为叶子节点,且和为targetSum时,返回true
        if(!topPair.node->left && !topPair.node->right && topPair.sum == targetSum)
            return true;

        // 若当前栈顶节点有左右孩子,计算和并入栈
        if(topPair.node->left) {
            struct Pair newPair = {topPair.node->left, topPair.sum + topPair.node->left->val};
            stack[stackTop++] = newPair;
        }
        if(topPair.node->right) {
            struct Pair newPair = {topPair.node->right, topPair.sum + topPair.node->right->val};
            stack[stackTop++] = newPair;
        }
    }
    return false;
}

113.路径总和II

题目链接:113. 路径总和 II - 力扣(LeetCode)

题目描述:给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。

叶子节点 是指没有子节点的节点。

示例 1:

img

输入:root = [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum = 22
输出:[[5,4,11,2],[5,8,4,5]]

示例 2:

img

输入:root = [1,2,3], targetSum = 5
输出:[]

示例 3:

输入:root = [1,2], targetSum = 0
输出:[]

提示:

  • 树中节点总数在范围 [0, 5000]
  • -1000 <= Node.val <= 1000
  • -1000 <= targetSum <= 1000

DFS 递归

int** ret;
int* path;
int* colSize;
int retTop;
int pathTop;

void traversal(const struct TreeNode* const node, int count) {
    // 若当前节点为叶子节点
    if(!node->right && !node->left) {
        // 若当前path上的节点值总和等于targetSum。
        if(count == 0) {
            // 复制当前path
            int *curPath = (int*)malloc(sizeof(int) * pathTop);
            memcpy(curPath, path, sizeof(int) * pathTop);
            // 记录当前path的长度为pathTop
            colSize[retTop] = pathTop;
            // 将当前path加入到ret数组中
            ret[retTop++] = curPath;
        }
        return;
    }

    // 若节点有左/右孩子
    if(node->left) {
        // 将左孩子的值加入path中
        path[pathTop++] = node->left->val;
        traversal(node->left, count - node->left->val);
        // 回溯
        pathTop--;
    }
    if(node->right) {
        // 将右孩子的值加入path中
        path[pathTop++] = node->right->val;
        traversal(node->right, count - node->right->val);
        // 回溯
        --pathTop;
    }
}

int** pathSum(struct TreeNode* root, int targetSum, int* returnSize, int** returnColumnSizes){
    // 初始化数组
    ret = (int**)malloc(sizeof(int*) * 1000);
    path = (int*)malloc(sizeof(int*) * 1000);
    colSize = (int*)malloc(sizeof(int) * 1000);
    retTop = pathTop = 0;
    *returnSize = 0;

    // 若根节点不存在,返回空的ret
    if(!root)
        return ret;
    // 将根节点加入到path中
    path[pathTop++] = root->val;
    traversal(root, targetSum - root->val);

    // 设置返回ret数组大小,以及其中每个一维数组元素的长度
    *returnSize = retTop;
    *returnColumnSizes = colSize;

    return ret;
}

106.从中序与后序遍历序列构造二叉树

题目链接:106. 从中序与后序遍历序列构造二叉树 - 力扣(LeetCode)

题目描述:给定两个整数数组 inorderpostorder ,其中 inorder 是二叉树的中序遍历, postorder 是同一棵树的后序遍历,请你构造并返回这颗二叉树

示例 1:

img

输入:inorder = [9,3,15,20,7], postorder = [9,15,7,20,3]
输出:[3,9,20,null,null,15,7]

示例 2:

输入:inorder = [-1], postorder = [-1]
输出:[-1]

提示:

  • 1 <= inorder.length <= 3000
  • postorder.length == inorder.length
  • -3000 <= inorder[i], postorder[i] <= 3000
  • inorderpostorder 都由 不同 的值组成
  • postorder 中每一个值都在 inorder
  • inorder 保证是树的中序遍历
  • postorder 保证是树的后序遍历

思路

以 后序数组的最后一个元素为切割点,先切中序数组,根据中序数组,反过来再切后序数组。一层一层切下去,每次后序数组最后一个元素就是节点元素。

  • 第一步:如果数组大小为零的话,说明是空节点了。
  • 第二步:如果不为空,那么取后序数组最后一个元素作为节点元素。
  • 第三步:找到后序数组最后一个元素在中序数组的位置,作为切割点
  • 第四步:切割中序数组,切成中序左数组和中序右数组 (顺序别搞反了,一定是先切中序数组)
  • 第五步:切割后序数组,切成后序左数组和后序右数组
  • 第六步:递归处理左区间和右区间

切割时要注意坚持循环不变量

int linearSearch(int* arr, int arrSize, int key) {
    int i;
    for(i = 0; i < arrSize; i++) {
        if(arr[i] == key)
            return i;
    }
    return -1;
}

struct TreeNode* buildTree(int* inorder, int inorderSize, int* postorder, int postorderSize){
    //若中序遍历数组中没有元素,则返回NULL
    if(!inorderSize)
        return NULL;
    //创建一个新的结点,将node的val设置为后序遍历的最后一个元素
    struct TreeNode* node = (struct TreeNode*)malloc(sizeof(struct TreeNode));
    node->val = postorder[postorderSize - 1];

    //通过线性查找找到中间结点在中序数组中的位置
    int index = linearSearch(inorder, inorderSize, postorder[postorderSize - 1]);

    //左子树数组大小为index
    //右子树的数组大小为数组大小减index减1(减的1为中间结点)
    int rightSize = inorderSize - index - 1;
    node->left = buildTree(inorder, index, postorder, index);
    node->right = buildTree(inorder + index + 1, rightSize, postorder + index, rightSize);
    return node;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值