代码随想录:二叉树的属性

二叉树的深度

递归法获得二叉树的深度

树的深度 -> 后序遍历求根节点的高度 

思路:递归三步骤。求根节点的高度,即最大深度,可以用后序遍历的逻辑

1.确定函数形式,返回类型,参数:输入一个TreeNode,返回一个int深度,so

int getMaxDepth(treeNode * node)

2. 确定终止条件: 如果node为空,就返回0,表示高度为0

if(node == NULL) return 0;

3. 确定递归逻辑:对一个节点而言,看左右子节点返回的高度,哪个高度大作为该节点的返回(+1是加了当前节点的高度1)

int leftDepth = getMaxDepth(node->left);
int rightDepth = getMaxDepth(node->right);
int maxDepth = 1 + max(leftDepth, rightDepth);
return maxDepth;

  

整合代码

class Solution {
public:
    int getdepth(TreeNode* root) {
        if (root == NULL) return 0;
        int leftdepth = getdepth(root->left);       //
        int rightdepth = getdepth(root->right);     //
        int depth = 1 + max(leftdepth, rightdepth); //
        return depth;
    }
    int maxDepth(TreeNode* root) {
        return getdepth(root);
    }
};

还可以精简代码:

class solution {
public:
    int maxdepth(treenode* root) {
        if (root == null) return 0;
        return 1 + max(maxdepth(root->left), maxdepth(root->right));
    }
};

 

前序遍历求深度

实际上前序遍历才能体现求深度的真正逻辑

class Solution {
public:
    int result;
    void getdepth(TreeNode* root, int depth){
        //前序遍历 中 左 右
        result = depth > result?depth : result;//中
        if(root->left==NULL &&root->right==NULL) return;
        if(root->left){//左
            depth++;
            getdepth(root->left,depth);
            depth--;//回溯
        }
        if(root->right){//右
            depth++;
            getdepth(root->right,depth);
            depth--;//回溯
        }
        return;
    }

    int maxDepth(TreeNode* root) {
        result = 0;
        if(root==NULL) return result;
        getdepth(root,1);
        return result;
    }
}

  

上面有步骤可以简化代码

if(root->left) getDepth(node->left, depth + 1);
if(root->right) getDepth(node->right, depth + 1);

  

迭代法获得二叉树的深度

 可以用层序遍历法来得到二叉树的最大深度和最小深度

二叉树的最大深度

104. 二叉树的最大深度 - 力扣(LeetCode) (leetcode-cn.com)

给定一个二叉树,找出其最大深度。

二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。

说明: 叶子节点是指没有子节点的节点

思路:层序遍历,当每遍历一层,就把深度加1,当遍历完最后一层,队列为空,退出。

/**
 * Definition for a binary tree node.
 * 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) {}
 * };
 */
class Solution {
public:
    int maxDepth(TreeNode* root) {
        queue<TreeNode*> queueNode;
        if(root != NULL) queueNode.push(root);
        //空二叉树拳头警告
        //vector<vector<int>> res;
        int depth = 0;
        while(!queueNode.empty()){
            //每次遍历一层,一层的节点在进入while时候确定,后面会改变,所以for循环中不能用size(),而应该用固定长度
            int layerSize = queueNode.size();
            for(int i =0;i<layerSize;i++){
                TreeNode *tempNode = queueNode.front();
                queueNode.pop();
                if(tempNode->left) queueNode.push(tempNode->left);
                if(tempNode->right) queueNode.push(tempNode->right);
            }
            depth+=1;
        }
        return depth;  
    }
};

 

二叉树的最小深度

111. 二叉树的最小深度 - 力扣(LeetCode) (leetcode-cn.com)

给定一个二叉树,找出其最小深度。

最小深度是从根节点到最近叶子节点的最短路径上的节点数量。

错的思路:当某层节点数不为2^(k-1)时,就可能为最小深度

 

思路:注意,只有当左右孩子都为空的时候,才说明遍历的最低点了。如果其中一个孩子为空则不是最低点

所以只有当某个节点左右孩子都指向NULL,才作为最小深度的层的节点,从顶层往下做,当遇到第一个左右子节点为空时,说明遇到了叶子节点,可直接返回深度

/**
 * Definition for a binary tree node.
 * 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) {}
 * };
 */
class Solution {
public:
    int minDepth(TreeNode* root) {
        queue<TreeNode*> queueNode;
        if(root != NULL) queueNode.push(root);
        if(root == NULL) return 0;
        //空二叉树拳头警告
        //vector<vector<int>> res;
        int minDepth = 1;
        while(!queueNode.empty()){
            //每次遍历一层,一层的节点在进入while时候确定,后面会改变,所以for循环中不能用size(),而应该用固定长度
            int layerSize = queueNode.size();
            
            for(int i =0;i<layerSize;i++){
                TreeNode *tempNode = queueNode.front();
                queueNode.pop();
                if(tempNode->left) queueNode.push(tempNode->left);
                if(tempNode->right) queueNode.push(tempNode->right);
                if(!tempNode->left && !tempNode->right) return minDepth;
            }
            minDepth+=1;
        }
        return minDepth;  
    }
};

 

对称二叉树

101. 对称二叉树 - 力扣(LeetCode) (leetcode-cn.com)

也叫镜像二叉树,即判断给定root的二叉树是不是镜像/对称的。

对称的二叉树:

 

不对称的二叉树:

 

 

递归判断对称二叉树

思路:递归。

关于中间对称轴对称的两节点是否相同,而不仅仅是左右节点相同。so比较对称的两棵子树是否是相互翻转的。。

一个递归 左右中,一个递归 右左中,来比较对称的子树(左边看和右边看过去是一样的)

 

既然是递归方法,递归三部曲:背(确定函数参数和返回值,确定终止条件,确定递归逻辑)

确定函数参数和返回值

这里,要递归比较两棵对称的子树是否是相互翻转,参数为两棵子树的root,返回值是true 或者false.

bool cmp(TreeNode * left, TreeNode * right){
    //递归函数
}

确定终止条件

什么时候不对称呢返回false?外侧空节点不对称,内测空节点不对称,当前root的val不相等

什么时候对称呢返回true?左右节点都不为空,且数值相同的情况(p.s. 另外还有一种情况,左右子树都为空,即到了叶子节点)

if(left == NULL && right != NULL) return false;
else if (left != NULL && right == NULL) return false;
else if (left->val != right->val) return false;
else if (left == NULL && right == NULL) return true;

确定递归逻辑

处理左右子树都不为空,且数值相同的情况。

要递归比较左子树的外侧和右子树的外侧 left->left vs right->right

要递归比较左子树的内测和右子树的内测 left->right vs right->left

只有当以上两个比较都返回true时候,才返回true

bool outside = cmp(left->left, right->right);
bool inside = cmp(left->right, right->left);
return ouside&&inside;

  

完成代码:

/**
 * Definition for a binary tree node.
 * 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) {}
 * };
 */
class Solution {
public:
    bool cmp(TreeNode * left, TreeNode * right){
    //递归函数
    if(left == NULL && right != NULL) return false;
    else if (left != NULL && right == NULL) return false;
    else if (left == NULL && right == NULL) return true;
    else if (left->val != right->val) return false;//这一条要放到下面,不然可能会出现子节点为空就读取val的错误

    //左右对称节点不空,val相等的情况
    bool outside = cmp(left->left, right->right);
    bool inside = cmp(left->right, right->left);
    return outside && inside;
    }

    bool isSymmetric(TreeNode* root) {
        if(root==NULL) return true;
        return cmp(root->left, root->right);
    }
};

 

“迭代逻辑”判断对称二叉树

使用队列来遍历二叉树,把对应的元素按特定的顺序放入队列。每次比较两个节点,看看相对应的节点是否相等

class Solution {
public:
    bool isSymmetric(TreeNode* root) {
        if(root==NULL) return true;
        //队列存放比较节点
        queue<TreeNode*> que;
        que.push(root->left);
        que.push(root->right);
        while(!que.empty()){
            //把要比较的节点弹出来暂存 队列先进先出
            TreeNode * leftTemp = que.front();
            que.pop();
            TreeNode * rightTemp = que.front();
            que.pop();
            //当相对的两个节点都为空,则局部对称
            if(!leftTemp && !rightTemp) continue;
            //相对的两个节点一空一有值,或者是都不为空但是值不相等
            if(leftTemp&&!rightTemp || !leftTemp&&rightTemp || (leftTemp->val!=rightTemp->val)) return false;

            //因为上面两个if排除了为有空节点的情况,只剩下两个节点不为空,且值相等,所以push没毛病
            que.push(leftTemp->left);
            que.push(rightTemp->right);
            que.push(leftTemp->right);
            que.push(rightTemp->left);
        }
        return true;
    }
};

上面代码其实是把左右两个子树要比较的元素顺序放进一个容器,然后成对成对的取出来进行比较。那么除了队列,使用栈也可以啊,只要把容器替换一下,队列改为栈(因为成对成对弹出压入,不用改什么顺序)。

平衡二叉树

110. 平衡二叉树 - 力扣(LeetCode) (leetcode-cn.com)

给定一个二叉树,判断它是否是高度平衡的二叉树。本题中,一棵高度平衡二叉树定义为:

一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。

思路:重点是平衡怎么判断?左右子树的高度差绝对值小于等于1,既然是比较高度,递归+后序遍历就可以了:

递归函数的参数和返回值:返回的是当前节点的高度,参数为当前节点的指针

int getHeight(TreeNode * root){
      //递归函数  
}

终止条件:如果遇到空节点(叶子节点指向的NULL),即返回 0

if(root == NULL) return 0;

单层递归逻辑: 先递归读取左子树高度,再递归读取右子树高度,相比较返回大的,并且差值如果大于等于2就不是平衡二叉树(但是因为是递归算法,不是很好直接退出函数返回,使用一个flag:-1 如果某一次的高度差大于1,那就是不平衡,一直影响到最后的输出)

int leftHeight = getHeight(root->left);
if(leftHeight==-1) return -1;

int rightHeight = getHeight(root->right);
if(rightHeight==-1) return -1;

return abs(leftHeight - rightHeight) > 1? -1 :max(leftHeight, rightHeight)+1;

  

写出代码:

/**
 * Definition for a binary tree node.
 * 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) {}
 * };
 */
class Solution {
public:
    int getHeight(TreeNode* root){
        if(root == NULL) return 0;

        int leftHeight = getHeight(root->left);
        if(leftHeight==-1) return -1;
        int rightHeight = getHeight(root->right);
        if(rightHeight==-1) return -1;
        return abs(rightHeight-leftHeight) > 1? -1:max(rightHeight,leftHeight)+1;
    }
    bool isBalanced(TreeNode* root) {
        int heigh = getHeight(root);
        if(heigh==-1) return false;
        else return true;
    }
};

 

完全二叉树的节点个数

普通二叉树的节点个数

思路1,迭代法,统计每一层的节点个数,复杂度$O(n), O(n)$

class Solution {
public:
    int countNodes(TreeNode* root) {
        int res = 0;
        queue<TreeNode*> queueNode;
        if(root != NULL) queueNode.push(root);
        //空二叉树拳头警告
        while(!queueNode.empty()){
            //每次遍历一层,一层的节点在进入while时候确定,后面会改变,所以for循环中不能用size(),而应该用固定长度
            int layerSize = queueNode.size();
            res+=layerSize;
            vector<int> layerRes;
            for(int i =0;i<layerSize;i++){
                //先弹出队首元素,再check他的左右儿子
                TreeNode *tempNode = queueNode.front();
                queueNode.pop();
                layerRes.push_back(tempNode->val);
                if(tempNode->left) queueNode.push(tempNode->left);
                if(tempNode->right) queueNode.push(tempNode->right);
            }
            
        }
        return res;
    }
};

但是,看看AC之后的效率,内存消耗太大了,想办法提高空间复杂度O(n) -> O(\log(n))

 

 

 

思路2,递归法,复杂度$O(n), O(\log(n))$; 求节点个数,递归三部曲

函数参数和返回值:返回传入的节点作为根节点的子树有多少个节点,参数为当前节点

int countNodes(TreeNode* root){
     //递归函数  
}

终止条件:当root为NULL时,返回0

if(root==NULL) return 0;

单层递归逻辑

int leftNodes = get(root->left);
int rightNodes = get(root->right);
return leftNodes + rightNodes + 1;

  

写出代码:

class Solution {
public:
    int count(TreeNode* root){
        if(root == NULL) return 0;
        return count(root->left) + count(root->right) + 1;
    }
    int countNodes(TreeNode* root) {
        return count(root);
    }
};

还可以简化

class Solution {
public:
    int countNodes(TreeNode* root) {
        if(root == NULL) return 0;
        return count(root->left) + count(root->right) + 1;
    }
};

完全二叉树的节点个数优化上面算法

222. 完全二叉树的节点个数 - 力扣(LeetCode) (leetcode-cn.com)

给你一棵 完全二叉树 的根节点 root ,求出该树的节点个数。

完全二叉树 的定义如下:

在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 $1~ 2^h$ 个节点。p.s. 根节点为第0层

 那么,相对于普通二叉树,可以根据二叉树性质优化什么呢?

  1. 如果是满二叉树,深度为k,那么节点个数为 $2^k-1$.
  2. 如果是缺最后一层右侧节点的满二叉树,分别递归左孩子,和右孩子,递归到某一深度一定会有左孩子或者右孩子为满二叉树,然后依然可以按照上面情况来计算。但如果不是满二叉树,就老老实实 $左+右+1$

即要么递归返回,要么使用满二叉树根据深度求节点返回(问题是既要返回节点个数又要求某个节点的深度)

求当前节点深度可以,根据满二叉树的性质和完全二叉树的性质,写如下代码:

int leftHeight = 0, rightHeight = 0; // 这里初始为0是有目的的,为了下面求指数方便
while (left) {  // 求左子树高度
    left = left->left;
    leftHeight++;
}
while (right) { // 求右子树高度
    right = right->right;
    rightHeight++;
}
  • 时间复杂度:$O(\log n × \log n)$
  • 空间复杂度:$O(\log n)$
/**
 * Definition for a binary tree node.
 * 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) {}
 * };
 */
class Solution {
public:

    int countNodes(TreeNode* root) {
        if(root == NULL) return 0;
        TreeNode* left = root->left;
        TreeNode* right = root->right;
        int leftHeight = 0, rightHeight = 0; // 这里初始为0是有目的的,为了下面求指数方便
        while (left) {  // 求左子树高度
            left = left->left;
            leftHeight++;
        }
        while (right) { // 求右子树高度
            right = right->right;
            rightHeight++;
        }
        if(leftHeight==rightHeight) return (2<<leftHeight)-1;



        return countNodes(root->left) + countNodes(root->right) + 1;
    }
};

 

二叉树的所有路径

257. 二叉树的所有路径 - 力扣(LeetCode) (leetcode-cn.com)

给你一个二叉树的根节点 root ,按 任意顺序 ,返回所有从根节点到叶子节点的路径。

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

思路:递归(回溯)+前序遍历(为了方便父节点指向子节点);

递归函数参数和返回值:记录路径,用string输出,所以参数为当前节点、记录路径的容器、记录输出的容器。

void catchPath(TreeNode* cur, vector<int>& path, vector<int>&res){
      //递归函数  
}

终止条件:当递归到叶子节点的时候就回溯

if(cur->left==NULL && cur->right==NULL){
    //终止逻辑  
}

具体的终止逻辑就是,当遇到叶子的时候,把当前路径加到res,

string tempPath;
for(int i=0;i<path.size()-1;i++){
     tempPath += to_string(path[i]);
     tempPath += "->";    
}
tempPath += to_string(path[path.size()-1]);
res.push_back(tempPath);
return;

  

单层递归逻辑: 在哪里做回溯

递归完,为什么要做回溯?因为path 不能一直加入节点,它还要删节点,然后才能加入新的节点。一个递归对应一个回溯

if (cur->left) {
    catchPath(cur->left, path, res);
    path.pop_back(); // 回溯
}
if (cur->right) {
    catchPath(cur->right, path, res);
    path.pop_back(); // 回溯
}

  

写出代码:

/**
 * Definition for a binary tree node.
 * 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) {}
 * };
 */
class Solution {
public:
    void catchPath(TreeNode* cur, vector<int>& path, vector<string>&res){
        //更新路径
        path.push_back(cur->val);
        //递归函数  
        if(cur->left==NULL && cur->right==NULL){
            //终止逻辑  
            string tempPath;
            for(int i=0;i<path.size()-1;i++){
                tempPath += to_string(path[i]);
                tempPath += "->";    
            }
            tempPath += to_string(path[path.size()-1]);
            res.push_back(tempPath);
            return;
        }  
        if (cur->left) {
            catchPath(cur->left, path, res);
            path.pop_back(); // 回溯
        }
        if (cur->right) {
            catchPath(cur->right, path, res);
            path.pop_back(); // 回溯
        } 
    }
    vector<string> binaryTreePaths(TreeNode* root) {
        vector<string> res;
        vector<int> path;
        if (root == NULL) return res;
        catchPath(root,path,res);
        return res;
    }
};

 

左叶子之和

找树左下角的值

路径总和

posted @ 2022-03-08 15:44  PiaYie  阅读(50)  评论(0编辑  收藏  举报