Leetcode刷题笔记(C++)——二叉树

Leetcode刷题笔记(C++)——二叉树

整理一下刷题过程中的思路,在这里进行一下总结与分享。
github地址:https://github.com/lvjian0706/Leetcode-solutions
github项目是刚刚新建的,陆续会将整理的代码以及思路上传上去,代码是基于C++与python的。同时会将基础的排序算法等也一并进行整理上传。

102. 二叉树的层序遍历

给你一个二叉树,请你返回其按 层序遍历 得到的节点值。 (即逐层地,从左到右访问所有节点)。

示例:
二叉树:[3,9,20,null,null,15,7],
3
/
9 20
/
15 7
返回其层次遍历结果:
[
[3],
[9,20],
[15,7]
]

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    /*
    层序遍历:宽度优先搜索(bfs)
    1. 创建队列存放二叉树的节点以及节点对应的level,其中使用pair来存放二叉树的节点以及节点对应的level;
    2. 如果树为空,返回空ans数组;
    3. 首先将进栈,并且定义idx为当前level;
    4. 循环遍历队头中的元素:
    4.1 使用pnode存储队头元素,保存队头元素的值以及level,并将队头元素出栈;
    4.2 判断队头元素的level与idx是否一致,一致说明队头元素与之前的元素属于相同的level,直接push进subAns;否则说明队头元素与之前的元素属于不同的level,更新idx,先将subAnspush进ans中,再清空subAns,并将队头元素的值push进去;
    4.3 判断是否还有左右孩子,有的话将左右孩子和对应的level(nowIdx+1)存入队列;
    5. 将最后一组subAns的值push进ans中,返回ans;
    */
    vector<vector<int>> levelOrder(TreeNode* root) {
        queue<pair<TreeNode*, int>> qnode;
        vector<vector<int>> ans;
        if(!root) return ans;
        vector<int> subAns;
        qnode.push(make_pair(root, 0));
        int idx = 0;
        while(!qnode.empty()){
            pair<TreeNode*, int> pnode = qnode.front();
            qnode.pop();
            TreeNode* node = pnode.first;
            int nowIdx = pnode.second;
            if(nowIdx != idx){
                ans.push_back(subAns);
                subAns.clear();
                idx = nowIdx;
            }
            subAns.push_back(node->val);
            if(node->left) qnode.push(make_pair(node->left, nowIdx+1));
            if(node->right) qnode.push(make_pair(node->right, nowIdx+1));
        }
        ans.push_back(subAns);
        return ans;
    }
};

103. 二叉树的锯齿形层次遍历

给定一个二叉树,返回其节点值的锯齿形层次遍历。(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行)。

例如:
给定二叉树 [3,9,20,null,null,15,7],
3
/
9 20
/
15 7
返回锯齿形层次遍历如下:
[
[3],
[20,9],
[15,7]
]

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    /*
    层序遍历:宽度优先搜索(bfs)
    1. 创建队列存放二叉树的节点以及节点对应的level,其中使用pair来存放二叉树的节点以及节点对应的level;
    2. 如果树为空,返回空ans数组;
    3. 首先将进栈,并且定义idx为当前level;
    4. 定义flag变量用于判断当前行是从左往右还是从右往左进行遍历;
    5. 循环遍历队头中的元素:
    5.1 使用pnode存储队头元素,保存队头元素的值以及level,并将队头元素出栈;
    5.2 判断队头元素的level与idx是否一致,一致说明队头元素与之前的元素属于相同的level,通过flag的状态判断是将当前元素push到数组末尾还是insert到数组头部;
    5.3. 否则说明队头元素与之前的元素属于不同的level,更新idx,先将subAns插入ans的第一个位置,再清空subAns,并通过flag的状态判断是将当前元素push到数组末尾还是insert到数组头部;;
    5.4 判断是否还有左右孩子,有的话将左右孩子和对应的level(nowIdx+1)存入队列;
    6. 将最后一组subAns的值插入ans的第一个位置,返回ans;
    */
    vector<vector<int>> zigzagLevelOrder(TreeNode* root) {
        vector<vector<int>> ans;
        if(!root) return ans;
        vector<int> subAns;
        queue<pair<TreeNode*, int>> nodeQ;
        nodeQ.push(make_pair(root, 0));
        int idx = 0;
        bool flag = true;
        while(!nodeQ.empty()){
            TreeNode* node = nodeQ.front().first;
            int nowIdx = nodeQ.front().second;
            nodeQ.pop();
            if(nowIdx!=idx){
                ans.push_back(subAns);
                subAns.clear();
                flag = !flag;
                idx = nowIdx;
            }
            if(flag) subAns.push_back(node->val);
            else subAns.insert(subAns.begin(), node->val);
            if(node->left) nodeQ.push(make_pair(node->left, nowIdx+1));
            if(node->right) nodeQ.push(make_pair(node->right, nowIdx+1));
        }
        ans.push_back(subAns);
        return ans;
    }
};

104. 二叉树的最大深度

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

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

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

示例:
给定二叉树 [3,9,20,null,null,15,7],
3
/
9 20
/
15 7
返回它的最大深度 3 。

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    /*
    深度优先搜索:
    1. 传入参数:当前节点以及上一层节点的深度;
    2. 如果当前节点为空,返回上一层节点的深度;
    3. 否则,采用分治思想,递归计算左子树的深度和右子树的深度,返回深度最大值;
    */
    int dfs(TreeNode* root, int depth){
        if(!root) return depth;
        int leftDepth = dfs(root->left, depth+1);
        int rightDepth = dfs(root->right, depth+1);
        return max(leftDepth, rightDepth);
    }

    /*
    树的遍历:求树的最长路径的深度,可以使用深度优先搜索(dfs)
    */
    int maxDepth(TreeNode* root) {
        int ans = dfs(root, 0);
        return ans;
    }
};

107. 二叉树的层次遍历 II

给定一个二叉树,返回其节点值自底向上的层次遍历。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)

例如:
给定二叉树 [3,9,20,null,null,15,7],
3
/
9 20
/
15 7
返回其自底向上的层次遍历为:
[
[15,7],
[9,20],
[3]
]

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    /*
    层序遍历:宽度优先搜索(bfs)
    1. 创建队列存放二叉树的节点以及节点对应的level,其中使用pair来存放二叉树的节点以及节点对应的level;
    2. 如果树为空,返回空ans数组;
    3. 首先将进栈,并且定义idx为当前level;
    4. 循环遍历队头中的元素:
    4.1 使用pnode存储队头元素,保存队头元素的值以及level,并将队头元素出栈;
    4.2 判断队头元素的level与idx是否一致,一致说明队头元素与之前的元素属于相同的level,直接push进subAns;否则说明队头元素与之前的元素属于不同的level,更新idx,先将subAns插入ans的第一个位置,再清空subAns,并将队头元素的值push进去;
    4.3 判断是否还有左右孩子,有的话将左右孩子和对应的level(nowIdx+1)存入队列;
    5. 将最后一组subAns的值插入ans的第一个位置,返回ans;
    */
    vector<vector<int>> levelOrderBottom(TreeNode* root) {
        vector<vector<int>> ans;
        if(!root) return ans;
        vector<int> subAns;
        queue<pair<TreeNode*, int>> nodeQ;
        nodeQ.push(make_pair(root, 0));
        int idx = 0;
        while(!nodeQ.empty()){
            TreeNode* node = nodeQ.front().first;
            int nowIdx = nodeQ.front().second;
            nodeQ.pop();
            if(nowIdx!=idx){
                ans.insert(ans.begin(), subAns);
                subAns.clear();
                idx = nowIdx;
            }
            subAns.push_back(node->val);
            if(node->left) nodeQ.push(make_pair(node->left, nowIdx+1));
            if(node->right) nodeQ.push(make_pair(node->right, nowIdx+1));
        }
        ans.insert(ans.begin(), subAns);
        return ans;
    }
};

110. 平衡二叉树

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

本题中,一棵高度平衡二叉树定义为:

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

示例 1:
给定二叉树 [3,9,20,null,null,15,7]
3
/
9 20
/
15 7
返回 true 。
示例 2:
给定二叉树 [1,2,2,3,3,null,null,4,4]
1
/
2 2
/
3 3
/
4 4
返回 false 。

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    /*
    计算二叉树的深度:
    1. 如果不存在该节点,返回0;
    2. 计算左子树深度和右子树深度;
    3. 该树的深度为左子树深度和右子树深度的最大值加1(1代表根节点深度);
    */
    int height(TreeNode* root){
        if(!root) return 0;
        int left = height(root->left);
        int right = height(root->right);
        return max(left, right)+1;
    }

    /*
    判断二叉树是否平衡:考虑到需要判断二叉树中每个节点并计算深度,可以使用深度优先搜索(dfs)
    1. 如果不存在节点,返回true;
    2. 判断左子树和右子树是否是平衡二叉树;
    3. 计算左子树深度和右子树深度;
    4. 当左子树和右子树都是平衡二叉树,且左子树深度和右子树深度差的绝对值不超过1时,返回true,否则,返回false;
    */
    bool isBalanced(TreeNode* root) {
        if(!root) return true;
        bool leftBanlanced = isBalanced(root->left);
        bool rightBanlanced = isBalanced(root->right);
        int lheight = height(root->left);
        int rheight = height(root->right);
        if(leftBanlanced && rightBanlanced && lheight <= rheight+1 && rheight <= lheight+1){
            return true;
        }
        else return false;
    }
};

124. 二叉树中的最大路径和

给定一个非空二叉树,返回其最大路径和。

本题中,路径被定义为一条从树中任意节点出发,达到任意节点的序列。该路径至少包含一个节点,且不一定经过根节点。

示例 1:
输入: [1,2,3]
1
/
2 3
输出: 6
示例 2:
输入: [-10,9,20,null,null,15,7]
-10
/
9 20
/
15 7
输出: 42

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    /*
    计算每个节点的贡献值,同时使用全局变量maxSum记录遍历到当前节点时的最大路径和:
    1. 当该节点为空时,贡献值为0;
    2. 计算该节点左子树和右子树的贡献值,其中,需要保证贡献值大于等于0(贡献值为负数时,不将该子树的路径和加入到最终的结果中,因此,相当于加0);
    3. 计算该节点的最大路径和,更新maxSum;
    4. 返回该节点的贡献值(该节点的值加max(左子树的最大路径和,右子树的最大路径和));
    */
    int nodeGain(TreeNode* root, int &maxSum){
        if(!root) return 0;
        int leftSum = max(nodeGain(root->left, maxSum), 0);
        int rightSum = max(nodeGain(root->right, maxSum), 0);
        int nowSum = root->val + leftSum + rightSum;
        maxSum = max(maxSum, nowSum);
        return root->val + max(leftSum, rightSum);
    }
    /*
    最大路径和:需要进行深度遍历,使用深度优先搜索(dfs)
    1. 定义maxSum变量实时保存每个节点的最大路径和;
    2. 计算每个节点的贡献值,同时使用全局变量maxSum记录遍历到当前节点时的最大路径和:
    3. 返回maxSum;
    */
    int maxPathSum(TreeNode* root) {
        int maxSum = INT_MIN;
        int rootPathSum = nodeGain(root, maxSum);
        return maxSum;
    }
};

236. 二叉树的最近公共祖先

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

示例 1:
输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出: 3
解释: 节点 5 和节点 1 的最近公共祖先是节点 3。
示例 2:
输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出: 5
解释: 节点 5 和节点 4 的最近公共祖先是节点 5。因为根据定义最近公共祖先节点可以为节点本身。

说明:
所有节点的值都是唯一的。
p、q 为不同节点且均存在于给定的二叉树中。

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    /*
    最近公共祖先:可以用深度优先遍历(dfs)
    注意:p、q 为不同节点且均存在于给定的二叉树中;
    1. 如果节点为空,直接返回NULL;
    2. 当遍历到p或q时,直接返回该节点;(找到了其中一个节点)
    3. 递归遍历,如果左子树中遍历到了某一个节点,返回该节点;右子树同理;
    4. 当左子树和右子树都存在某个节点时,返回根节点root;(找到了最近公共节点)
    5. 当只有左子树存在节点时,返回左子树的节点;(有可能只找到了一个节点,也有可能两个节点都在左子树);右子树同理;
    6. 否则,不存在两个节点,返回null;
    由于是自底向上,而且一定会存在p和q节点,因此在递归过程中最近公共节点会覆盖子节点
    */
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(!root) return NULL;
        if(root->val==p->val || root->val==q->val) return root;
        TreeNode* left = lowestCommonAncestor(root->left, p, q);
        TreeNode* right = lowestCommonAncestor(root->right, p, q);
        if(left!=NULL && right!=NULL) return root;
        else if(left!=NULL) return left;
        else if(right!=NULL) return right;
        else return NULL;
    }
};


/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    /*
    递归遍历节点,判断该节点是否是p或q的祖先,同时更新ans:
    1. 如果root不存在,返回false;
    2. 判断左孩子是否为p和q的公共节点以及右孩子是否为p和q的公共节点;
    3. 如果左右孩子同时为p和q的公共节点,或者root是q或者p中的一个且左右孩子中有一个为p和q的公共节点,则说明root为p和q的最近公共节点;
    4. 如果root是q或者p中的一个或者左右孩子中有一个为p和q的公共节点,则说明该节点是p或q的祖先,返回true;
    由于是自底向上判断,且一定存在p和q两个节点,因此找到的一定是最近公共节点;
    */
    bool isAncestor(TreeNode* root, TreeNode* p, TreeNode* q, TreeNode* &ans){
        if(!root) return false;
        bool isleft = isAncestor(root->left, p, q, ans);
        bool isright = isAncestor(root->right, p, q, ans);
        if((isleft && isright) || ((root->val==q->val || root->val==p->val) && (isleft || isright))){
            ans = root;
        }
        return isleft || isright || (root->val==q->val || root->val==p->val);
    }

    /*
    最近公共祖先:可以使用深度遍历的方法,使用深度优先搜索(dfs)
    1. 定义变量ans存放最近公共节点
    2. 定义函数isAncestor递归遍历节点,判断该节点是否是p或q的祖先,同时更新ans
    */
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        TreeNode* ans;
        bool isroot = isAncestor(root, p, q, ans);
        return ans;
    }
};

你可能感兴趣的:(Leetcode刷题笔记(C++)——二叉树)