谈谈二叉树算法

 解决问题思路有两个方向

=> 1. 利用遍历框架,在前中后位置加入新的逻辑

=> 2. 利用分解问题的思路,将问题分解为 当前结点 和 左右子树

前中后遍历

前序遍历:根节点 + 左子树前序遍历的结果 + 右子树前序遍历的结果

中序遍历:左子树中序遍历的结果 + 根节点 +  右子树中序遍历的结果

后序遍历:左子树后序遍历的结果 + 右子树后序遍历的结果 + 根节点

(是不是从文字上就能看出递归的猫腻?)

void Traverse(TreeNode* root) {
    if(!root) return;
//    前序位置
    Traverse(root->left);
//    中序位置
    Traverse(root->right);
//    后序位置
}

以前序遍历为例:

vector<int> res;
void Traverse(TreeNode* root) {
    if(!root) return;
    res.emplace_back(root->val);
    Traverse(root->left);
    Traverse(root->right);
}
vector<int> preorderTraverse(TreeNode* root) {
    Traverse(root);
    return res;
}

如果我想用从分解问题(其实就是分治)的角度去模拟前序遍历的结果呢?

vector<int> preorder(TreeNode* root) {
    vector<int> ans;
    if(!root) return ans;
//    前序遍历的结果:根节点 + 左子树前序遍历的结果 + 右子树前序遍历的结果(言下之意:层层向下拨开云雾至子叶点)
    vector<int> left = preorder(root->left);
    ans.insert(ans.end(), left.begin(), left.end());
    vector<int> right = preorder(root->right);
    ans.insert(ans.end(), right.begin(), right.end());
    return ans;
}

543. 二叉树的直径 - 力扣(LeetCode)

借助遍历框架解决问题

注意depth的定义:该层的深度大小

前序是刚来到这个结点 => 深度+1,后序是要离开这个结点啦 => 深度 - 1

// ans记录的是整个遍历下来depth的最大值
int ans = 0;
// depth记录的是该层的深度大小
int depth = 0;
void traverse(TreeNode* root) {
    if(!root) return;
    depth++;
    ans = max(ans, depth);
    traverse(root->left);
    traverse(root->right);
    depth--;
}
int maxDepthTraverse(TreeNode* root) {
    traverse(root);
    return ans;
}

分治思想解决问题

递归真是一种魔法!

=> 可以实现倒序

=> 可以用来拆分问题,把大问题拆解成一个一个能解决的小问题,综合一下,返回最终答案

这里每个maxLeft或者maxRight就看成一颗新的小一点的树继续执行这个任务

int maxDepthRecursion(TreeNode* root) {
//    分治
    if(!root) return 0;
    int maxLeft = maxDepthRecursion(root->left);
    int maxRight = maxDepthRecursion(root->right);
//    整棵树的最大深度 == 左右子树中的最大深度 + 1(根节点自己)
    return max(maxLeft, maxRight) + 1;
}

 再来深入理解一下后序位置的特殊性

前序位置的代码只能从函数参数中获取父节点传递来的数据,

后序位置的代码不仅可以获取参数数据,还可以获取到子树通过函数返回值传递回来的数据

一旦你发现题目和子树有关,那大概率要给函数设置合理的定义和返回值,在后序位置写

 543. 二叉树的直径 - 力扣(LeetCode)

class Solution {
public:
//  理解题意:某一结点的直径 == 左子树深度 + 右子树深度
//  => 有没有读出 遍历 + 分治 的味道?
    int MaxDiameter = 0;
    int maxDepth(TreeNode* root) {
        if(!root) return 0;
        int leftMax = maxDepth(root->left);
        int rightMax = maxDepth(root->right);
        // 后序位置
        MaxDiameter = max(MaxDiameter, leftMax + rightMax);
        return max(leftMax, rightMax) + 1;
    }

    int diameterOfBinaryTree(TreeNode* root) {
        maxDepth(root);
        return MaxDiameter;
    }
};

层序遍历

从上到下遍历二叉树的每一层

从左到右遍历每一层的每个节点

迭代写法比较好理解

void levelTraverse(TreeNode* root) {
    if(!root) return;
    queue<TreeNode*> q;
    q.push(root);
    while(!q.empty()) {
        int size = q.size();
        for(int i = 0; i < size; i++) {
            auto cur = q.front();
            cout << cur->val;
            q.pop();
            if(cur->left) q.push(cur->left);
            if(cur->right) q.push(cur->right);
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值