二叉树 | 2.二叉树的属性

零. 大纲:

在这里插入图片描述

参考

  1. 代码随想录


一. 深度 & 高度

  • 深度:根节点 到该节点的最长简单路径边的条数。
    前序遍历(中左右)

  • 高度:该节点到 叶子节点 的最长简单路径边的条数
    后序遍历(左右中)

根节点的高度就是二叉树的最大深度
在这里插入图片描述
例子见: [104.二叉树的最大深度]


1.1 104.二叉树的最大深度

  • 深度:根节点 到该节点的最长简单路径边的条数。
    前序遍历(中左右)
  • 高度:该节点 到 叶子节点 的最长简单路径边的条数
    后序遍历(左右中)
    根节点的高度就是二叉树的最大深度,

在这里插入图片描述
递归法
根节点的高度就是二叉树的最大深度,

所以本题中我们通过后序求的根节点高度来求的二叉树最大深度

后序 求 高度

// 1.确定递归函数的参数和返回值:参数就是传入树的根节点,
//   返回就返回这棵树的深度,所以返回值为int类型。
//   int getdepth(treenode* node)
// 2. 确定终止条件:如果为空节点的话,就返回0,表示高度为0。
//   if (node == NULL) return 0;
// 3. 确定单层递归的逻辑:先求它的左子树的深度,再求右子树的深度,
//    最后取左右深度最大的数值 再+1 
//   (加1是因为算上当前中间节点)就是目前节点为根节点的树的深度。


class solution {
public:
    int getdepth(treenode* node) {
        if (node == NULL) return 0;
        int leftdepth = getdepth(node->left);       // 左
        int rightdepth = getdepth(node->right);     // 右
        int depth = 1 + max(leftdepth, rightdepth); // 中
        return depth;
    }
    
    int maxdepth(treenode* root) {
        return getdepth(root);
    }
};

//精简之后
//	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* node, int depth) {
        result = depth > result ? depth : result; // 中

        if (node->left == NULL && node->right == NULL) return ;

        if (node->left) { // 左
            depth++;    // 深度+1
            getdepth(node->left, depth);
            depth--;    // 回溯,深度-1
        }
        if (node->right) { // 右
            depth++;    // 深度+1
            getdepth(node->right, depth);
            depth--;    // 回溯,深度-1
        }
        return ;
    }
    int maxdepth(treenode* root) {
        result = 0;
        if (root == NULL) return result;
        getdepth(root, 1);
        return result;
    }
};

迭代法: 层序遍历

class solution {
public:
    int maxdepth(treenode* root) {
        if (root == NULL) return 0;
        int depth = 0;
        queue<treenode*> que;
        que.push(root);
        while(!que.empty()) {
            int size = que.size();
            depth++; // 记录深度
            for (int i = 0; i < size; i++) {
                treenode* node = que.front();
                que.pop();
                if (node->left) que.push(node->left);
                if (node->right) que.push(node->right);
            }
        }
        return depth;
    }
};

559. N 叉树的最大深度

在这里插入图片描述

  • N叉树 与 二叉树区别:
    二叉树:TreeNode* node
    node -> left
    node -> right
    N叉树: 子节点不止两个

递归法,深度优先搜索

class Solution {
public:
    int maxDepth(Node* root) {
        if (root == nullptr) {
            return 0;
        }
        
        int maxChildDepth = 0;
        vector<Node *> children = root->children;
        
        for (auto child : children) {
            int childDepth = maxDepth(child);
            maxChildDepth = max(maxChildDepth, childDepth);
        }
        return maxChildDepth + 1;
    }
};

// 精简版本
int maxDepth(Node* root) {
        if (root == 0) return 0;
        int depth = 0;
        for (int i = 0; i < root->children.size(); i++) {
            depth = max (depth, maxDepth(root->children[i]));
            // depth = (maxDepth(root->children[i]) > depth ? 
            //           maxDepth(root->children[i]) : depth ); 
            // 超时
        }
        return depth + 1;
        
    }

迭代法,广度优先搜索

class Solution {
public:
    int maxDepth(Node* root) {
        if (root == nullptr) {
            return 0;
        }
        queue<Node *> qu;
        qu.push(root);
        int ans = 0;
        while (!qu.empty()) {
            int size = qu.size();
            while (size > 0) {
                Node * node = qu.front();
                qu.pop();
                vector<Node *> children = node->children;
                for (auto child : children) {
                    qu.push(child);
                }
                size--;
            }
            ans++;
        }
        return ans;
    }
};

1.2 111. 二叉树的最小深度

在这里插入图片描述
最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
递归法

class Solution {
public:
    int getDepth(TreeNode* node) {
        if (node == NULL) return 0;
        int leftDepth = getDepth(node->left);           // 左
        int rightDepth = getDepth(node->right);         // 右
                                                        // 中
        // 当一个左子树为空,右不为空,这时并不是最低点
        if (node->left == NULL && node->right != NULL) { 
            return 1 + rightDepth;
        }   
        // 当一个右子树为空,左不为空,这时并不是最低点
        if (node->left != NULL && node->right == NULL) { 
            return 1 + leftDepth;
        }
        int result = 1 + min(leftDepth, rightDepth);
        return result;
    }

    int minDepth(TreeNode* root) {
        return getDepth(root);
    }
};

迭代法

class Solution {
public:
    int minDepth(TreeNode* root) {
        if(root==NULL) return 0;
        int count=0;
        queue<TreeNode*> que;
        que.push(root);
        while(!que.empty()){
            int size = que.size();
            count++;
            for(int i=0; i<size;i++){
                TreeNode* node = que.front();
                que.pop();
                //if(!(node->left || node->right ))  return count;
                if (!node->left && !node->right) return count;
                if(node->left) que.push(node->left);
                if(node->right) que.push(node->right);
                
            }
        }
        return count;
        
    }
};



二. 101.对称二叉树

递归法

class Solution {
public:
    bool compare(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;

        // 此时就是:左右节点都不为空,且数值相同的情况
        // 此时才做递归,做下一层的判断
        bool outside = compare(left->left, right->right);
        bool inside = compare(left->right, right->left);
        bool isSame = outside && inside;
        return isSame;
    }
    bool isSymmetric(TreeNode* root) {
        if(root == NULL ) return true;
        return compare(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* leftNode = que.front(); que.pop();
            TreeNode* rightNode = que.front(); que.pop();
            if (!leftNode && !rightNode) {  // 左节点为空、右节点为空,此时说明是对称的
                continue;
            }

            // 左右一个节点不为空,或者都不为空但数值不相同,返回false
            if ((!leftNode || !rightNode || (leftNode->val != rightNode->val))) {
                return false;
            }
            // outside
            que.push(leftNode->left);   // 加入左节点左孩子
            que.push(rightNode->right); // 加入右节点右孩子
            // inside
            que.push(leftNode->right);  // 加入左节点右孩子
            que.push(rightNode->left);  // 加入右节点左孩子
        }
        return true;
    }
};

572.另一个树的子树

参考:【简单明晰】C++ 判断子树
如果root所在树和subRoot所在树完全相同,则直接返回true。
否则,就分别判断root的左子树和右子树是否和subRoot所在树完全相同。

class Solution {
public:
    bool isSameTree(TreeNode* p, TreeNode* q) {
        if (p == nullptr && q == nullptr)
            return true;
        if (p && q && p->val == q->val)
            return isSameTree(p->left, q->left) && isSameTree(p->right, q->right);
        else
            return false;
    }
    bool isSubtree(TreeNode* root, TreeNode* subRoot) {
        if (!root) //root不可能走到空
            return false;
        
        if (isSameTree(root, subRoot))
            return true;

        return isSubtree(root->left, subRoot) || isSubtree(root->right, subRoot); //左边为真就不用求右边了,所以是或关系
    }
};

100. 相同的树

深度优先搜索
如果两个二叉树都为空,则两个二叉树相同。如果两个二叉树中有且只有一个为空,则两个二叉树一定不相同。

如果两个二叉树都不为空,那么首先判断它们的根节点的值是否相同,若不相同则两个二叉树一定不同,若相同,再分别判断两个二叉树的左子树是否相同以及右子树是否相同。这是一个递归的过程,因此可以使用深度优先搜索,递归地判断两个二叉树是否相同。

class Solution {
public:
    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);
        }
    }
};


三. 222. 完全二叉树的节点个数

当普通二叉树处理

递归法
时间复杂度:O(n)
空间复杂度:O(log n),算上了递归系统栈占用的空间

class Solution {
private:
    int getNodesNum(TreeNode* cur) {
        if (cur == NULL) return 0;
        int leftNum = getNodesNum(cur->left);      // 左
        int rightNum = getNodesNum(cur->right);    // 右
        int treeNum = leftNum + rightNum + 1;      // 中
        return treeNum;
    }
public:
    int countNodes(TreeNode* root) {
        return getNodesNum(root);
    }
};

迭代法
时间复杂度:O(n)
空间复杂度:O(n)

class Solution {
public:
    int countNodes(TreeNode* root) {
        queue<TreeNode*> que;
        if (root != NULL) que.push(root);
        int result = 0;
        while (!que.empty()) {
            int size = que.size();
            for (int i = 0; i < size; i++) {
                TreeNode* node = que.front();
                que.pop();
                result++;   // 记录节点数量
                if (node->left) que.push(node->left);
                if (node->right) que.push(node->right);
            }
        }
        return result;
    }
};

利用完全二叉树特点

  • 完全二叉树只有两种情况,情况一:就是满二叉树,情况二:最后一层叶子节点没有满。
    对于情况一,可以直接用 2^树深度 - 1 来计算,注意这里根节点深度为1。
    对于情况二,分别递归左孩子,和右孩子,递归到某一深度一定会有左孩子或者右孩子为满二叉树,然后依然可以按照情况1来计算。
    在这里插入图片描述

时间复杂度:O(log n × log n)
空间复杂度:O(log n)

class Solution {
public:
    int countNodes(TreeNode* root) {
        if (root == nullptr) return 0;
        TreeNode* left = root->left;
        TreeNode* right = root->right;
        int leftDepth = 0, rightDepth = 0; // 这里初始为0是有目的的,为了下面求指数方便
        while (left) {  // 求左子树深度
            left = left->left;
            leftDepth++;
        }
        while (right) { // 求右子树深度
            right = right->right;
            rightDepth++;
        }
        if (leftDepth == rightDepth) {
            return (2 << leftDepth) - 1; // 注意(2<<1) 相当于2^2,所以leftDepth初始为0
        }
        return countNodes(root->left) + countNodes(root->right) + 1;
    }
};

理解:

在这里插入图片描述



四. 110. 平衡二叉树

给定一个二叉树,判断它是否是高度平衡的二叉树。

一棵高度平衡二叉树定义为:
一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。
在这里插入图片描述
递归

class Solution {
public:
    // 返回以该节点为根节点的二叉树的高度,如果不是平衡二叉树了则返回-1
    int getHeight(TreeNode* node) {
        if (node == NULL) {
            return 0;
        }

        int leftHeight = getHeight(node->left);       // 左
        if (leftHeight == -1) return -1;

        int rightHeight = getHeight(node->right);     // 右
        if (rightHeight == -1) return -1;

        int result;
        if ( abs(leftHeight - rightHeight) > 1 ) {     // 中
            result = -1;
        } else {
            result = 1 + max(leftHeight, rightHeight); // 以当前节点为根节点的树的最大高度
        }
        return result;
    }

    bool isBalanced(TreeNode* root) {
        return getHeight(root) == -1 ? false : true ;
    }
};

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

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

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

迭代法 效率较低

可以使用层序遍历来求深度,但是就不能直接用层序遍历来求高度了,这就体现出求高度和求深度的不同。

  • 效率低:
    因为没有很好的模拟回溯的过程,所以迭代法有很多重复的计算。
    虽然理论上所有的递归都可以用迭代来实现,但是有的场景难度可能比较大。
    例如:都知道回溯法其实就是递归,但是很少人用迭代的方式去实现回溯算法!
    因为对于回溯算法已经是非常复杂的递归了,如果再用迭代的话,就是自己给自己找麻烦,效率也并不一定高。

  • 本题的迭代方式可以先定义一个函数,专门用来求高度。
    这个函数通过栈模拟的后序遍历找每一个节点的高度(其实是通过求传入节点为根节点的最大深度来求的高度)

  • 然后再用栈来模拟后序遍历,遍历每一个节点的时候,再去判断左右孩子的高度是否符合

class Solution {
private:
    int getDepth(TreeNode* cur) {
        stack<TreeNode*> st;
        if (cur != NULL) st.push(cur);
        int depth = 0; // 记录深度
        int result = 0;
        while (!st.empty()) {
            TreeNode* node = st.top();
            if (node != NULL) {
                st.pop();
                st.push(node);                          // 中
                st.push(NULL);
                depth++;
                if (node->right) st.push(node->right);  // 右
                if (node->left) st.push(node->left);    // 左

            } else {
                st.pop();
                node = st.top();
                st.pop();
                depth--;
            }
            result = result > depth ? result : depth;
        }
        return result;
    }

public:
    bool isBalanced(TreeNode* root) {
        stack<TreeNode*> st;
        if (root == NULL) return true;
        st.push(root);
        while (!st.empty()) {
            TreeNode* node = st.top();                       // 中
            st.pop();
            if (abs(getDepth(node->left) - getDepth(node->right)) > 1) {
                return false;
            }
            if (node->right) st.push(node->right);           // 右(空节点不入栈)
            if (node->left) st.push(node->left);             // 左(空节点不入栈)
        }
        return true;
    }
};


五. 路径问题

参考:

  1. 代码随想录
  2. 一篇文章解决所有二叉树路径问题(问题分析+分类模板+题目剖析)
    作者:eh-xing-qing
    来源:力扣(LeetCode)

问题分类

解题模板

这类题通常用深度优先搜索(DFS)和广度优先搜索(BFS)解决,BFS较DFS繁琐,这里为了简洁只展现DFS代码

一、自顶而下:

dfs
一般路径:

vector<vector<int>>res;
void dfs(TreeNode*root,vector<int>path)
{
    if(!root) return;  //根节点为空直接返回
    path.push_back(root->val);  //作出选择
    if(!root->left && !root->right) //如果到叶节点  
    {
        res.push_back(path);
        return;
    }
    dfs(root->left,path);  //继续递归
    dfs(root->right,path);
}

给定和的路径:

void dfs(TreeNode*root, int sum, vector<int> path)
{
    if (!root)
        return;
    sum -= root->val;
    path.push_back(root->val);
    if (!root->left && !root->right && sum == 0)
    {
        res.push_back(path);
        return;
    }
    dfs(root->left, sum, path);
    dfs(root->right, sum, path);
}

这类题型DFS注意点:

  • 1、如果是找路径和等于给定target的路径的,那么可以不用新增一个临时变量cursum来判断当前路径和,只需要用给定和target减去节点值,最终结束条件判断target==0即可

  • 2、是否要回溯:二叉树的问题大部分是不需要回溯的,原因如下:
    二叉树的递归部分:dfs(root->left),dfs(root->right)已经把可能的路径穷尽了,
    因此到任意叶节点的路径只可能有一条,绝对不可能出现另外的路径也到这个满足条件的叶节点的;
    而对比二维数组(例如迷宫问题)的DFS,for循环向四个方向查找每次只能朝向一个方向,并没有穷尽路径,
    因此某一个满足条件的点可能是有多条路径到该点的
    并且visited数组标记已经走过的路径是会受到另外路径是否访问的影响,这时候必须回溯

  • 3、找到路径后是否要return: 取决于题目是否要求找到叶节点满足条件的路径,如果必须到叶节点,那么就要return;
    但如果是到任意节点都可以,那么必不能return,因为这条路径下面还可能有更深的路径满足条件,还要在此基础上继续递归

  • 4、是否要双重递归(即调用根节点的dfs函数后,继续调用根左右节点的pathsum函数):看题目要不要求从根节点开始的,还是从任意节点开始


二、非自顶而下:

这类题目一般解题思路如下:
设计一个辅助函数maxpath,调用自身求出以一个节点为根节点的左侧最长路径left和右侧最长路径right,那么经过该节点的最长路径就是left+right
接着只需要从根节点开始dfs,不断比较更新全局变量即可

int res=0;
int maxPath(TreeNode *root) //以root为路径起始点的最长路径
{
    if (!root)
        return 0;
    int left=maxPath(root->left);
    int right=maxPath(root->right);
    res = max(res, left + right + root->val); //更新全局变量  
    return max(left, right);   //返回左右路径较长者
}

这类题型DFS注意点:
1、left,right代表的含义要根据题目所求设置,比如最长路径、最大路径和等等

2、全局变量res的初值设置是0还是INT_MIN要看题目节点是否存在负值,如果存在就用INT_MIN,否则就是0

3、注意两点之间路径为1,因此一个点是不能构成路径的


递归函数返回值问题

递归函数什么时候要有返回值,什么时候没有返回值?特别是有的时候递归函数返回类型为bool类型。

  • 如果需要搜索 整棵二叉树 用处理递归返回值,递归函数就不要返回值
    113. 路径总和 II
  • 如果需要搜索整棵二叉树 需要 处理递归返回值 ,递归函数就需要返回值。
    236. 二叉树的最近公共祖先
    ()
  • 如果要搜索其中一条符合条件的路径 ,那么递归一定需要 返回值 ,因为遇到符合条件的路径了就要及时返回。
    112. 路径总和


5.1 257. 二叉树的所有路径

迭代法:
很好理解

class Solution {
public:
    vector<string> binaryTreePaths(TreeNode* root) {
        // 迭代法
        vector<string> result;  // 保存最终路径集合
        //用栈
        stack<TreeNode*> treeSt;// 保存树的遍历节点
        stack<string> pathSt;   // 保存遍历路径的节点

        if (root == NULL) return result;
        treeSt.push(root);
        pathSt.push(to_string(root->val));

        while (!treeSt.empty()) {
            TreeNode* node = treeSt.top(); treeSt.pop(); // 取出节点 中
            string path = pathSt.top(); pathSt.pop();    // 取出该节点对应的路径
            if (node->left == NULL && node->right == NULL) { // 遇到叶子节点
                result.push_back(path);
            }
            if (node->right) { // 右
                treeSt.push(node->right);
                pathSt.push(path + "->" + to_string(node->right->val));
            }
            if (node->left) { // 左
                treeSt.push(node->left);
                pathSt.push(path + "->" + to_string(node->left->val));
            }
        }
        return result;
    }
};

递归法(理解回溯)

注意点:

  • 递归终止条件
  • 回溯
  • 前序遍历
class Solution {
private:

    void traversal(TreeNode* cur, vector<int>& path, vector<string>& result) {
        path.push_back(cur->val); // 中,中为什么写在这里,因为最后一个节点也要加入到path中 
        // 这才到了叶子节点
        if (cur->left == NULL && cur->right == NULL) {
            string sPath;
            for (int i = 0; i < path.size() - 1; i++) {
                sPath += to_string(path[i]);
                sPath += "->";
            }
            sPath += to_string(path[path.size() - 1]);
            result.push_back(sPath);
            return;
        }
        // 如果中 写在这里,即,判断终止条件之后的话, 
        //那么叶子节点 会因为符合了 终止条件而 直接return 跳过了
        
        if (cur->left) { // 左 
            traversal(cur->left, path, result);
            path.pop_back(); // 回溯
        }
        if (cur->right) { // 右
            traversal(cur->right, path, result);
            path.pop_back(); // 回溯
        }
    }

public:
    vector<string> binaryTreePaths(TreeNode* root) {
        vector<string> result;
        vector<int> path;
        if (root == NULL) return result;
        traversal(root, path, result);
        return result;
    }
};

模板1:

class Solution {
public:
   	vector<string> res;
    vector<string> binaryTreePaths(TreeNode* root)
    {
        dfs(root, "");
        return res;
    }

    void dfs(TreeNode*root, string path)
    {
        if (!root)
            return;
        path += to_string(root->val);
        if (!root->left && !root->right)
        {
            res.push_back(path);
            return;
        }
        dfs(root->left, path+"->");
        dfs(root->right, path+"->");
    }

};

5.2 404. 左叶子之和

注意点:

  • 左叶子,不是二叉树左侧节点,所以不要上来想着层序遍历
  • 左叶子的明确定义:节点A的左孩子不为空,且左孩子的左右孩子都为空(说明是叶子节点),那么A节点的左孩子为左叶子节点
  • 注意,目标是左叶子值,所以,终止条件中的return 0, 都是指的是左叶子值是0;

递归三部曲:

  1. 确定递归函数的参数和返回值
    判断一个树的左叶子节点之和,那么一定要传入树的根节点,递归函数的返回值为数值之和,所以为int

  2. 确定终止条件
    如果遍历到空节点,那么左叶子值一定是0
    注意,只有当前遍历的节点是父节点,才能判断其子节点是不是左叶子。 所以如果当前遍历的节点是叶子节点,那其左叶子也必定是0,那么终止条件为:

    if (root == NULL) return 0;
    if (root->left == NULL && root->right== NULL) return 0; 
    //其实这个也可以不写,如果不写不影响结果,但就会让递归多进行了一层。
    
  3. 确定单层递归的逻辑!!!
    当遇到左叶子节点的时候,记录数值,
    然后通过递归求取左子树左叶子之和,和 右子树左叶子之和,
    相加便是整个树的左叶子之和。

    int leftValue = sumOfLeftLeaves(root->left);    // 左
    
    // 单层逻辑
    // 左子树就是一个左叶子的情况
    if (root->left && !root->left->left && !root->left->right) {
    		leftValue = root->left->val;
    	}
    int rightValue = sumOfLeftLeaves(root->right);  // 右
    
    int sum = leftValue + rightValue;               // 中
    return sum;
    

完整代码

int sumOfLeftLeaves(TreeNode* root) {
        // 递归
        // 终止条件
        if(root ==NULL) return 0;
        if(root ->left == NULL && root->right == NULL)  return 0;

        // 单层逻辑
        // 左分支
        int leftValue = sumOfLeftLeaves(root->left);    // 左

        if ( root->left && !root->left->left && !root->left->right) { // 左子树就是一个左叶子的情况
            leftValue = root->left->val;
        }

        int rightValue = sumOfLeftLeaves(root->right);  // 右

        int sum = leftValue + rightValue;               // 中
        return sum;

    }
};

迭代法(前序):

class Solution {
public:
    int sumOfLeftLeaves(TreeNode* root) {
        stack<TreeNode*> st;
        if (root == NULL) return 0;
        st.push(root);
        int result = 0;
        while (!st.empty()) {
            TreeNode* node = st.top();
            st.pop();
            if (node->left != NULL && node->left->left == NULL && node->left->right == NULL) {
                result += node->left->val;
            }
            if (node->right) st.push(node->right);
            if (node->left) st.push(node->left);
        }
        return result;
    }
};

5.3 513. 找树左下角的值

递归(理解回溯)

递归三部曲

  • 1.确定递归函数的参数和返回值
    参数必须有要遍历的树的根节点,还有就是一个int型的变量用来记录最长深度。
    本题还需要类里的两个全局变量,maxDepth用来记录最大深度,result记录最大深度最左节点的数值。
  • 2.确定终止条件
    当遇到叶子节点的时候,就需要统计一下最大的深度了,需要遇到叶子节点来更新最大深度
  • 3.确定单层递归的逻辑
    traversal 更新一个maxDepth, result 取 maxDepth的值
class Solution {
public:
    int maxDepth = INT_MIN;             // maxDepth用来记录最大深度
    int result;                         // result记录最大深度最左节点的数值

    void traversal(TreeNode* root, int depth) {
        if (root->left == NULL && root->right == NULL) {
            if (depth > maxDepth) {
                maxDepth = depth;
                result = root->val;
            }
            return;
        }
                                            // 中

        if (root->left) {                   // 左
            depth++;
            traversal(root->left, depth);
            depth--; // 回溯
        }
		// 精简回溯							// 左
		//if (root->left) {                 // 左
        //    traversal(root->left, depth+1 );
        //}

        if (root->right) {                  // 右
            depth++;
            traversal(root->right, depth);
            depth--; // 回溯
        }
        // 精简回溯							// 右
		//if (root->right) {                 // 右
        //    traversal(root->right, depth+1 );
        //}
        return ;
    }

    int findBottomLeftValue(TreeNode* root) {
        traversal(root, 0);
        // 注意: trversal函数,没有输出,就是一个处理函数,处理了result;
        return result;
    }
};

迭代法(模板)

class Solution {
public:
    int findBottomLeftValue(TreeNode* root) {
        queue<TreeNode*> que;
        if (root != NULL) que.push(root);
        int result = 0;
        while (!que.empty()) {
            int size = que.size();
            for (int i = 0; i < size; i++) {
                TreeNode* node = que.front();
                que.pop();
                if (i == 0) result = node->val; // 记录最后一行第一个元素
                if (node->left) que.push(node->left);
                if (node->right) que.push(node->right);
            }
        }
        return result;
    }
};

5.4 112. 路径总和

递归三部曲:

  • 1.确定递归函数的参数和返回类型
    参数:需要二叉树的根节点,还需要一个计数器,这个计数器用来计算二叉树的一条边之和是否正好是目标和,计数器为int型。

    bool traversal(treenode* cur, int count)   // 注意函数的返回类型
    
  • 2.确定终止条件
    首先计数器如何统计这一条路径的和呢?
    不要去累加然后判断是否等于目标和,那么代码比较麻烦,可以用递减,让计数器count初始为目标和,然后每次减去遍历路径节点上的数值。
    如果最后count == 0,同时到了叶子节点的话,说明找到了目标和。
    如果遍历到了叶子节点,count不为0,就是没找到。

    if (!cur->left && !cur->right && count == 0) return true; // 遇到叶子节点,并且计数为0
    if (!cur->left && !cur->right) return false; // 遇到叶子节点而没有找到合适的边,直接返回
    
  • 3.确定单层递归的逻辑
    因为终止条件是判断叶子节点,所以递归的过程中就不要让空节点进入递归了。
    递归函数是有返回值的,如果递归函数返回true,说明找到了合适的路径,应该立刻返回。

    if (cur->left) { // 左
    	count -= cur->left->val; // 递归,处理节点;
    	if (traversal(cur->left, count)) return true;
    	count += cur->left->val; // 回溯,撤销处理结果
    }
    if (cur->right) { // 右
    	count -= cur->right->val;
    	if (traversal(cur->right, count)) return true;
    	count += cur->right->val;
    }
    return false;
    

整体代码:

class Solution {
private:
    bool traversal(TreeNode* cur, int count) {
        if (!cur->left && !cur->right && count == 0) return true; 
        // 遇到叶子节点,并且计数为0
        if (!cur->left && !cur->right) return false; 
        // 遇到叶子节点直接返回

        if (cur->left) { // 左
            count -= cur->left->val; // 递归,处理节点;
            if (traversal(cur->left, count)) return true;
            count += cur->left->val; // 回溯,撤销处理结果
        }
        if (cur->right) { // 右
            count -= cur->right->val; // 递归,处理节点;
            if (traversal(cur->right, count)) return true;
            count += cur->right->val; // 回溯,撤销处理结果
        }
        return false;
    }

public:
    bool hasPathSum(TreeNode* root, int sum) {
        if (root == NULL) return false;
        return traversal(root, sum - root->val);
    }
};

迭代法:pair结构

如果使用栈模拟递归的话,那么如果做回溯呢?
此时栈里一个元素不仅要记录该节点指针,还要记录从头结点到该节点的路径数值总和。

c++就我们用pair结构来存放这个栈里的元素。
定义为:pair<TreeNode*, int> pair<节点指针,路径数值>

class solution {

public:
    bool haspathsum(TreeNode* root, int sum) {
        if (root == null) return false;
        // 此时栈里要放的是pair<节点指针,路径数值>
        stack< pair<TreeNode*, int> > st;
        st.push(pair<TreeNode*, int>(root, root->val));
        
        while (!st.empty()) {
            pair<TreeNode*, int> node = st.top();
            st.pop();
            // 如果该节点是叶子节点了,同时该节点的路径数值等于sum,那么就返回true
            if (!node.first->left && !node.first->right && sum == node.second) {
            	return true;
            	}

            // 右节点,压进去一个节点的时候,将该节点的路径数值也记录下来
            if ( node.first->right ) {
                st.push( pair<TreeNode*, int> ( node.first->right, node.second + node.first->right->val));
            }

            // 左节点,压进去一个节点的时候,将该节点的路径数值也记录下来
            if (node.first->left) {
                st.push(pair<TreeNode*, int>(node.first->left, node.second + node.first->left->val));
            }
        }
        return false;
    }
};

5.5 113. 路径总和 II

在这里插入图片描述

class Solution {
public:
// 113.路径总和ii要遍历整个树,找到所有路径,所以递归函数不要返回值!
    vector<vector<int>> result;
    vector<int> path;
    void traversal(TreeNode* cur, int count) {
        // 终止条件
        if ( !cur->left && !cur->right && count == 0 ) { 
        // 遇到了叶子节点且找到了和为sum的路径
            result.push_back(path);
            return;
        }

        if (!cur->left && !cur->right) return ;          
        // 遇到叶子节点而没有找到合适的边,直接返回

        if (cur->left) {                    // 左 (空节点不遍历)
            path.push_back(cur->left->val);
            count -= cur->left->val;
            traversal(cur->left, count);    // 递归
            count += cur->left->val;        // 回溯
            path.pop_back();                // 回溯
        }
        if (cur->right) {                   // 右 (空节点不遍历)
            path.push_back( cur->right->val  );
            count -= cur->right->val;
            traversal( cur->right, count ); // 递归
            count += cur->right->val;       // 回溯
            path.pop_back();                // 回溯
        }
        return ;
    }

    vector<vector<int>> pathSum(TreeNode* root, int sum) {
        result.clear();
        path.clear();
        if (root == NULL) return result;
        path.push_back(root->val); // 把根节点放进路径
        traversal(root, sum - root->val);
        return result;
    }
};

模板2:(内存消耗较大)

class Solution {
public:
    vector<vector<int>> res;
    vector<vector<int>> pathSum(TreeNode *root, int targetSum)
	{
    	vector<int> path;
    	dfs(root, targetSum, path);
    	return res;
	}

	void dfs(TreeNode*root, int sum, vector<int> path)
	{
    	if (!root)
        	return;
    	sum -= root->val;
    	path.push_back(root->val);
    	if (!root->left && !root->right && sum == 0)
    	{
        	res.push_back(path);
        	return;
    	}
   		dfs(root->left, sum, path);
    	dfs(root->right, sum, path);
}
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值