二叉树操作

多为力扣题目,旨在熟悉二叉树的各种操作,以及深搜时通过子树汇总信息的思想

定义树:

struct TreeNode {
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode() : val(0), left(nullptr), right(nullptr) {}
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
    TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
};

计算高度(最大深度)

问题:传入一个根节点,计算该树的高度

int maxDepth(TreeNode* root) {
    if(!root) return 0;
    return max(maxDepth(root->left), maxDepth(root->right)) + 1;
}

计算最小深度

递归实现

int minDepth(TreeNode* root)
{
    if(!root){
        return 0;
    }
    if(!root->left && !root->right){
        return 1;
    }

    int minn = 1e9; // 找左右子树的最小
    if(root->left){
        minn = min(minDepth(root->left),minn);
    }
    if(root->right){
        minn = min(minDepth(root->right),minn);
    }

    return minn+1;
}

迭代实现:思路比较清晰,逐层操作

计算直径

二叉树的直径是指树中任意两个节点之间最长路径的长度,两节点之间路径的长度由它们之间边数表示

注意:这条最长路径可能不经过根节点

解法:遍历每个节点,计算该节点的两个子树的最大深度和,取最大者

计算坡度

  • 一个树的节点的坡度定义为,该节点左子树的节点之和和右子树节点之和的差的绝对值
  • 如果没有左子树的话,左子树的节点之和为 0;没有右子树的话也是一样。空结点的坡度是 0
  • 整个树 的坡度就是其所有节点的坡度之和

官方题解

int ans = 0;
int findTilt(TreeNode* root) {
    dfs(root);
    return ans;
}

int dfs(TreeNode* root){
    if(!root) return 0;
    int lsum = dfs(root->left);
    int rsum = dfs(root->right);
    ans += abs(lsum - rsum);
    return root->val + lsum + rsum; // 返回给上层节点使用
}

判断相同

问题:传入两个根节点,判断这两棵树是否相同

可能会想到通过前序和中序遍历结果来判断,但如果两棵树的每个元素都相同而只是结构不同,则所有遍历方式的结果都相同;除非加入判断左右子树的标志,而这种情况下只使用一种遍历方式结果

bool isSameTree(TreeNode* p, TreeNode* q) {
    return pre(p) == pre(q);
}

string pre(TreeNode* root){
    if(root == nullptr){
        return "";
    }
    string s;
    s.append(to_string(root->val));
    s+='l';
    s.append(pre(root->left));
    s+='r';
    s.append(pre(root->right));
    return s;
}

官方题解:

bool isSameTree(TreeNode* p, TreeNode* q) {
    if (p == nullptr && q == nullptr) {
        return true;
    } else if (p == nullptr || q == nullptr) {
        return false;
    } else if (p->val != q->val) {
        return false;
    } else {
        return isSameTree(p->left, q->left) && isSameTree(p->right, q->right);
    }
}

判断对称

问题:传入一个根节点,判断该树是否对称

对称树的特点是:对于每个节点,其左子树和右子树是对称的,即左子树的左节点等于右子树的右节点,左子树的右节点等于右子树的左节点

官方题解(递归):

bool isSymmetric(TreeNode* root) {
    return check(root->left, root->right);
}

bool check(TreeNode* u,TreeNode* v){
    if(!u && !v) return true;
    if(!u || !v || u->val != v->val) return false;
    return check(u->left, v->right) && check(u->right, v->left);
}

官方题解(迭代):

bool isSymmetric(TreeNode* root) {
    return check(root->left,root->right);
}

bool check(TreeNode* u,TreeNode* v){
    queue<TreeNode*> q;
    q.push(u); q.push(v);
    while(!q.empty()){
        u = q.front(); q.pop();
        v = q.front(); q.pop();
        if(!u && !v){
            continue;
        }
        if((!u || !v) || u->val != v->val){
            return false;
        }

        q.push(u->left);
        q.push(v->right);

        q.push(u->right);
        q.push(v->left);
    }
    return true;
}

判断平衡

平衡二叉树是指所有节点的左右子树的高度相差不超过 1 的二叉树

先上一个错误代码

int getHeight(TreeNode* root) {
    if(!root) return 0;
    return max(getHeight(root->left), getHeight(root->right)) + 1;
}

bool isBalanced(TreeNode* root) {
    if(!root){
        return true;
    }
    return abs(getHeight(root->left) - getHeight(root->right)) <= 1;
}

错误之处在于只判断了根,而没有考虑每个子树

        1      <-- 根节点:左高 3,右高 2。差 1。你的代码判定:平衡 (Pass)
       / \
      2   3    <-- 节点 2:左高 2,右高 0。差 2。实际情况:不平衡 (Fail)
     /     \
    4       5
   /
  6

正确做法要自顶向下递归,遍历每个节点,计算其左右子树的高度,并判断是否平衡

int getHeight(TreeNode* root) {
    if(!root) return 0;
    return max(getHeight(root->left), getHeight(root->right)) + 1;
}

bool isBalanced(TreeNode* root) {
    if(!root){
        return true;
    }
    if(abs(getHeight(root->left)-getHeight(root->right)) > 1){
        return false;
    }
    return isBalanced(root->left) && isBalanced(root->right);
}

路径总和

问题:断二叉树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和

先放个错误代码

bool hasPathSum(TreeNode* root, int targetSum) {
    if(!root) return false;
    return helper(root, targetSum, 0);
}

bool helper(TreeNode* root, int targetSum, int sum) {
    if(!root){
        if(sum == targetSum) return true;
        return false;
    }

    return helper(root->left, targetSum, sum + root->val)
            || helper(root->right, targetSum, sum + root->val);
}

错在把叶子节点的判断视为了 !root 的前驱

    1 // 目标和为 1,此时进入 1 的右子节点时会错误地把 1 当作叶子结点
   /
  2

我的AC代码:

bool hasPathSum(TreeNode* root, int targetSum) {
    if(!root) return false;
    return helper(root, targetSum, 0);
}

bool helper(TreeNode* root, int targetSum, int sum) {
    if(!root) return false;
    if(!root->left && !root->right){ // 修正
        if(sum+root->val == targetSum) return true;
        return false;
    }

    return helper(root->left, targetSum, sum + root->val)
            || helper(root->right, targetSum, sum + root->val);
}

官方题解

bool hasPathSum(TreeNode* root, int targetSum) {
    if(!root) return false;
    targetSum -= root->val;
    if(!root->left && !root->right) return targetSum == 0;
    return hasPathSum(root->left, targetSum) || hasPathSum(root->right, targetSum);
}

如果要把所有路径都求出来呢?

class Solution {
public:
   vector<vector<int>> ret;
   vector<int> path;

   void dfs(TreeNode* root, int targetSum) {
       if (root == nullptr) {
           return;
       }
       path.emplace_back(root->val);
       targetSum -= root->val;
       if (root->left == nullptr && root->right == nullptr && targetSum == 0) {
           ret.emplace_back(path);
       }
       dfs(root->left, targetSum);
       dfs(root->right, targetSum);
       path.pop_back(); // <-- 注意这里
   }

   vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
       dfs(root, targetSum);
       return ret;
   }
};

最近公共祖先 // todo

思路:后序遍历。左边找到了吗?右边找到了吗?如果左右都找到了,那我就是祖先。


以下为与二叉搜索树相关


验证二叉搜索树

当然,利用中序遍历有序的方式也可以进行验证,但这里讨论其他方法

先放个错误代码

bool isValidBST(TreeNode* root) {
    if (root == nullptr) return true;
    if (root->left && root->left->val >= root->val) return false;
    if (root->right && root->right->val <= root->val) return false;
    return isValidBST(root->left) && isValidBST(root->right);
}

只能通过大部分样例(77/86),误区在于该代码只比较了上下层的关系,跨层关系未进行判断

  10
 /  \
5    15
    /
   6  <-- 错误在这里

而二叉树搜索树定义为:“左子树所有节点值 <= 根节点值,右子树所有节点值 >= 根节点值” 的二叉树,左、右子树也需满足该规则

比较的不应是上下层,而是根与左右子树各节点的最值

我的初版AC代码:

bool isValidBST(TreeNode* root) {
    if(!root) return true;
    bool f1, f2;
    if(root->left){
        f1 = root->val >= maxsub(root->left);
    }
    else{
        f1 = true;
    }
    if(root->right){
        f2 = root->val <= minsub(root->right);
    }
    else{
        f2 = true;
    }
    return isValidBST(root->left) && isValidBST(root->right) && f1 && f2;
}

int maxsub(TreeNode* root){
    if(!root) return -1e9;
    int maxx = root->val;
    if(root->left){
        maxx = max(maxsub(root->left),maxx);
    }
    if(root->right){
        maxx = max(maxsub(root->right),maxx);
    }
    return maxx;
}
int minsub(TreeNode* root){
    if(!root) return 1e9;
    int minn = root->val;
    if(root->left){
        minn = min(minsub(root->left),minn);
    }
    if(root->right){
        minn = min(minsub(root->right),minn);
    }
    return minn;
}

当然,这个 isValidBST 还可以再简洁化:

bool isValidBST(TreeNode* root) {
    if(!root) return true;
    if(root->left && root->val <= maxsub(root->left)){
        return false;
    }
    if(root->right && root->val >= minsub(root->right)){
        return false;
    }
    return isValidBST(root->left) && isValidBST(root->right);
}

但依然比较丑陋,性能也比较低下。下面为官方题解

bool helper(TreeNode* root, long long lower, long long upper) {
    if(root == nullptr) return true;
    if(root->val <= lower || root -> val >= upper){
        return false;
    }
    return helper(root->left, lower, root->val) && helper(root->right, root->val, upper);
}
bool isValidBST(TreeNode* root) {
    return helper(root, -1e18, 1e18);
}

有序数组转二叉搜索树

问题:给定一个整数数组 nums,其中元素已经按升序排列,将其转换为一棵平衡二叉搜索树

选择中间数字作为二叉搜索树的根节点,这样分给左右子树的数字个数相同或只相差 1,可以使得树保持平衡

TreeNode* sortedArrayToBST(vector<int>& nums) {
    return helper(nums, 0, nums.size()-1);
}

TreeNode* helper(vector<int>& nums, int l, int r){
    if(l > r) return nullptr;
    int mid = (l+r)/2;
    TreeNode* root = new TreeNode(nums[mid]);
    root->left = helper(nums, l, mid-1);
    root->right = helper(nums, mid+1, r);
    return root;
}

有序链表转二叉搜索树? 同理可用上述方法,获取中间节点可以采用快慢指针的方式,快指针一次移两个单位,慢指针移一个,当快指针到达边界时,慢指针即指向中间节点 或者还有另一种也是通用的方法,bfs 建树 + dfs 填值

标题:二叉树操作

作者:Zwing

创建于:2026-01-16 19:10:18

更新于:2026-08-07 16:17:52

链接:https://zanytriumph.github.io/posts/二叉树操作.html

版权声明:本文章采用 CC BY-NC-SA 4.0 进行许可