leetcode 二叉树问题

二叉树与递归

一、二叉树相关的定义

  • 完全二叉树:除了最后一层所有层的节点数达到最大
  • 满二叉树:所有层的节点数达到最大
  • 平衡二叉树:每个节点的左右子树的高度差不超过1
  • 二分搜索数:每个节点的左子节点小于等于该节点,右子节点大于该节点

二、递归的两个部分

  • 递归终止条件
  • 递归过程
//
// Created by yzm on 12/6/18.
//

#include <iostream>
#include <vector>
#include <queue>
#include <string>
#include <stack>
#include <assert.h>
#include <stdexcept>
#include <limits.h>
#include <unordered_set>
#include <unordered_map>
#include <functional>//C++11新特性
#include <math.h>
using namespace std;


//Definition for a binary tree node.
struct TreeNode {
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};

struct ListNode {
    int val;
    ListNode *next;
    ListNode(int x) : val(x), next(NULL) {}
};

/**
 * 104.二叉树的最大深度
 * 二叉树的深度为根节点到最远子节点的最长路径上的节点数
 * */

// 思路: 递归思想,二叉树的最大深度就是根节点的最大深度,也就是左右子树中最大深度加上1,以上定义完美的符合递归的定义
class Solution1 {
public:
    int maxDepth(TreeNode* root) {

        if (root == NULL)
            return 0;

        return max(maxDepth(root->left), maxDepth(root->right)) + 1;
    }
};
/**
 * 111.二叉树的最小深度
 * */

// 思路: 还是沿用上面的递归,但是不是简单的将max变成min,因为最小深度的定义是首先是要有深度的,在有深度里取较小的深度,所以首先要判断当前节点的左右子节点的存在情况
class Solution2 {
public:
    int minDepth(TreeNode* root) {

        if (root == NULL)
            return 0;

        // 如果当前节点具有左右子节点
        if (root->left != NULL&&root->right != NULL) {
            return min(minDepth(root->left), minDepth(root->right)) + 1;
        }
        else if (root->left != NULL) {		// 如果只有其中一个
            return minDepth(root->left) + 1;
        }
        else if (root->right != NULL) {
            return minDepth(root->right) + 1;
        }
        else {	// 没有左右子节点
            return 1;
        }

    }
};

/**
 * 226.翻转二叉树
 * */
// 思路: 递归,翻转二叉树,翻转根节点的左右子树,翻转当前节点的左右子树
class Solution3 {
public:
    TreeNode* invertTree(TreeNode* root) {

        if (root == NULL)
            return NULL;

        // 左右子树进行翻转
        root->left = invertTree(root->left);
        root->right = invertTree(root->right);
        // 当前节点翻转
        swap(root->left, root->right);

        return root;
    }
};
/**
 * 100.相同的树
 * */

// 当前节点值一致,左右子树也是相同的树,
class Solution4 {
public:
    bool isSameTree(TreeNode *p, TreeNode *q) {

        if (p == NULL && q == NULL)
            return true;
        else if (p != NULL && q != NULL) {    // 递归情况
            if (p->val == q->val && isSameTree(p->left, q->left) && isSameTree(p->right, q->right))
                return true;
            else
                return false;
        } else
            return false;

    }

};
/**
 * 101.对称二叉树
 * 给定一个二叉树,检查它是否是镜像对称的
 * */
//与上题 相同,只是是自己的左树与右树进行比较
class Solution5 {
public:
    bool isSymmetric(TreeNode* root) {

        // 终止条件
        if (root == NULL)
            return true;

        return isSymmetric(root->left, root->right);

    }

    // 判断以p,q为根节点的两颗树是否对称
    static bool isSymmetric(TreeNode* p, TreeNode* q) {

        if (p == NULL && q == NULL)
            return true;
        if (p != NULL && q != NULL) {
            if (p->val == q->val) {
                if (isSymmetric(p->left, q->right) && isSymmetric(p->right, q->left))		// 这里这么递归对比就能很好的符合对称关系
                    return true;
                else
                    return false;
            }
            else return false;
        }
        else
            return false;

    }
};

/**
 *222.求完全二叉树的节点个数
 *
 * */

class Solution6 {
public:
    int countNodes(TreeNode* root) {

        // 这样超时了!!!
        /*if (root == NULL)
            return 0;
        return countNodes(root->left) + countNodes(root->right) + 1;*/




        // 思路2:求完全二叉树的结点个数。可以求左右子树的高度,
        //1.如果相等则说明左子树是满二叉树,那么可以根据左子树的高度求出左子树加上根节点的结点数为2^leftHeight,再加上右子树的结点即为以当前结点为根的结点总数
        //	2.否则说明右子树为满二叉树,同样右子树的结点加上根节点的结点数为2^rightHeight,再加上左子树结点即为当前结点为根的结点总数
        int res = 0;

        while (root != NULL) {

            int leftDepth = depth(root->left);
            int rightDepth = depth(root->right);
            if (leftDepth == rightDepth) {	// 说明左子树是完全二叉树
                res += pow(2, leftDepth);
                root = root->right;		// 变成了一个结构相同的子问题
            }
            else {
                res += pow(2, rightDepth);
                root = root->left;
            }
        }

        return res;
    }

    static int depth(TreeNode* root) {
        if (root == NULL)
            return 0;
        return depth(root->left) + 1;
    }
};
/**
 * 110. 平衡二叉树
 * */
// 思路: 判断树是否平衡,就判断根节点的两颗子树高度差是否在1之内,当然两颗子树要求也是平衡树才行,问题就转化成了如何去求解一根节点的深度
class Solution7 {
public:
    bool isBalanced(TreeNode* root) {

        if (root == NULL)
            return true;

        int t = depth(root->left) - depth(root->right);
        if (t >= -1 && t <= 1 && isBalanced(root->left) && isBalanced(root->right))
            return true;
        else return false;
    }

    static int depth(TreeNode* node) {
        if (node == NULL)
            return 0;

        return max(depth(node->left) + 1, depth(node->right) + 1);
    }

};
/**
 * 112.路径总和
 * */
// 思路: 判断到达叶子结点时,叶子结点的val是否就是剩余的sum值,是就true,否则就false,整个过程递归的检测左右子树是否有符合条件的路径
class Solution8 {
public:
    bool hasPathSum(TreeNode* root, int sum) {

        if (root == NULL)
            return false;

        if (root->left == NULL && root->right == NULL) {
            if (root->val == sum)
                return true;
            else
                return false;
        }

        return (hasPathSum(root->left, sum - root->val) || hasPathSum(root->right, sum - root->val));
    }
};
/**
 * 404.左叶子之和
 * */

class Solution9 {
public:
    int sumOfLeftLeaves(TreeNode* root) {

        if (root == NULL)
            return 0;

        int res = 0;

        if (root->left != NULL)		// 当前节点有左子节点
            if (root->left->left == NULL && root->left->right == NULL)	// 当前节点的左子节点是叶子结点
                res += root->left->val;

        res += sumOfLeftLeaves(root->left);
        // 继续在右子树中搜
        res += sumOfLeftLeaves(root->right);

        return res;
    }
};

/**
 * 257.二叉树的所有路径
 *
 * */
class Solution10 {
public:
    vector<string> binaryTreePaths(TreeNode* root) {

        if (root == NULL)
            return vector<string>();

        vector<string> res;

        // 找到了叶子结点,意味着要生成一条路径了
        if (root->left == NULL && root->right == NULL) {
            res.push_back(to_string(root->val));
            return res;
        }

        // 左子树的路径
        vector<string> leftS = binaryTreePaths(root->left);
        for (int i = 0; i < leftS.size(); i++) {
            res.push_back(to_string(root->val) + "->" + leftS[i]);
        }
        // 右子树的路径
        vector<string> rightS = binaryTreePaths(root->right);
        for (int i = 0; i < rightS.size(); i++) {
            res.push_back(to_string(root->val) + "->" + rightS[i]);
        }

        return res;
    }
};
/**
 * 113.路径总和2
 * */
// 思路: 是solution10和solution8的结合,终止的边界条件是sum,路径的记录方法是solution9的方式
class Solution11 {
public:
    vector<vector<int>> pathSum(TreeNode* root, int sum) {

        vector<vector<int>> res_tmp = _pathSum(root, sum);
        vector<vector<int>> res= res_tmp;

        // 形式整理输出,本来是倒序的
        for (int i = 0; i < res_tmp.size(); i++) {
            int len = res_tmp[i].size();
            for (int j = 0; j < len; j++) {
                res[i][j] = res_tmp[i][len - 1 - j];
            }
        }

        return res;
    }

    static vector<vector<int>> _pathSum(TreeNode* root, int sum) {

        if (root == NULL)
            return vector<vector<int>>();

        vector<vector<int>> res;

        // 找到了符合题意的叶子结点,意味着生成了一条路径
        if (root->left == NULL && root->right == NULL && root->val == sum) {
            vector<int> path;
            path.push_back(root->val);
            res.push_back(path);
        }

        // 当前节点的左子树的正确路径
        vector<vector<int>> leftPath = _pathSum(root->left, sum - root->val);
        for (int i = 0; i < leftPath.size(); i++) {
            leftPath[i].push_back(root->val);
        }
        // 当前节点的右子树的正确路径
        vector<vector<int>> rightPath = _pathSum(root->right, sum - root->val);
        for (int i = 0; i < rightPath.size(); i++) {
            rightPath[i].push_back(root->val);
        }
        // 整理成当前节点正确路径数的形式
        for (int i = 0; i < leftPath.size(); i++) {
            res.push_back(leftPath[i]);
        }
        for (int i = 0; i < rightPath.size(); i++) {
            res.push_back(rightPath[i]);
        }

        return res;
    }
};

/**
 * 129. 求根到叶子节点数字之和
 * 给定一个二叉树,它的每个结点都存放一个 0-9 的数字,每条从根到叶子节点的路径都代表一个数字。
 * */

//  思路: 是solution10的变异,只需将所有从根节点到叶子结点之间的路径的存储方式和最终的显示方式做了相应的改变
class Solution12 {
public:
    int sumNumbers(TreeNode* root) {

        vector<vector<int>> resVec = _sumNumbers(root);
        int res = 0;

        // 整理成输出的形式
        for (int i = 0; i < resVec.size(); i++) {
            for (int j = 0; j < resVec[i].size(); j++) {
                res += resVec[i][j] * pow(10, j);
            }
        }

        return res;
    }

    vector<vector<int>> _sumNumbers(TreeNode* root) {

        if (root == NULL)
            return vector<vector<int>>();

        vector<vector<int>> res;

        // 找到叶子结点,就是找到了路径上的终点
        if (root->left == NULL && root->right == NULL) {
            vector<int> path;
            path.push_back(root->val);
            res.push_back(path);
        }

        vector<vector<int>> leftPath = _sumNumbers(root->left);
        for (int i = 0; i < leftPath.size(); i++) {
            vector<int> tmp = leftPath[i];
            tmp.push_back(root->val);
            res.push_back(tmp);
        }

        vector<vector<int>> rightPath = _sumNumbers(root->right);
        for (int i = 0; i < rightPath.size(); i++) {
            vector<int> tmp = rightPath[i];
            tmp.push_back(root->val);
            res.push_back(tmp);
        }
        return res;
    }
};

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

// 思路: 其实就是寻找递归结构的问题
// 1. 如果p和q在左右子树中,那么root就是最近公共祖先节点;
// 2. 如果p,q其中有一个是当前的root,那么root就是最近公共祖先节点;
// 3. 如果p和q都在当前root的一侧,那么root可以递归变为root->相应侧子节点,形成了递归结构
class Solution13 {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {

        if (root == NULL)
            return NULL;

        if (p->val < root->val && q->val < root->val)
            return lowestCommonAncestor(root->left, p, q);
        if (p->val > root->val && q->val > root->val)
            return lowestCommonAncestor(root->right, p, q);

        return root;
    }
};
/**
 * 98. 验证二叉搜索树
 * 给定一个二叉树,判断其是否是一个有效的二叉搜索树。
 * */
// 思路: 其实二分查找树的中序遍历就是将树中的值按从小到大遍历了,如果不符合从小到大,那么就不是二叉搜索树
class Solution14 {
public:
    bool isValidBST(TreeNode* root) {

        if (root == NULL)
            return false;

        vector<int> resVec = inOrder(root);

        for (int i = 1; i < resVec.size(); i++) {
            if (resVec[i] <= resVec[i - 1])
                return false;
        }

        return true;
    }

    static vector<int> inOrder(TreeNode* root) {

        if (root == NULL)
            return vector<int>();

        vector<int> res;

        vector<int> resLeft = inOrder(root->left);
        for (int i = 0; i < resLeft.size(); i++)
            res.push_back(resLeft[i]);

        res.push_back(root->val);

        vector<int> resRight = inOrder(root->right);
        for (int i = 0; i < resRight.size(); i++)
            res.push_back(resRight[i]);

        return res;
    }
};

/**
 * 108. 将有序数组转换为二叉搜索树
 * 本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1
 * */
// 思路: 找出递归结构如下,nums中间位的数值赋予当前节点的值,nums两侧的值递归地赋予为当前节点两侧的子树
class Solution15 {
public:
    TreeNode* sortedArrayToBST(vector<int>& nums) {

        TreeNode* res = arrayToBST(nums, 0, nums.size()-1);
        return res;
    }

    TreeNode* arrayToBST(vector<int>& nums, int begin, int end) {

        if (begin > end)
            return NULL;

        int mid = begin + (end - begin) / 2;

        TreeNode* res = new TreeNode(nums[mid]);

        res->left = arrayToBST(nums, begin, mid - 1);

        res->right = arrayToBST(nums, mid + 1, end);

        return res;
    }

};

/**
 * 230. 二叉搜索树中第K小的元素
 * */

// 思路: 获取中序遍历的vector,直接得到第k个数即可。其实没必要获取得到所有的中序遍历结果之后再停止,可以直接记数到了k停止即可,这样后面的结果就不用统计了
class Solution16 {
public:
    int kthSmallest(TreeNode* root, int k) {

        vector<int> res;
        _inOrder(root, k, res);

        return res[k - 1];
    }

    void _inOrder(TreeNode* root, int k, vector<int>& res) {

        if (root == NULL)
            return;

        _inOrder(root->left, k, res);
        res.push_back(root->val);
        if (res.size() >= k)
            return;
        _inOrder(root->right, k, res);

        return;
    }


};

/**
 * 236.在二叉树中寻找两个节点的公共祖先
 * */
 //本宝宝不会,学成归来再来做

猜你喜欢

转载自blog.csdn.net/qq_40028201/article/details/89451640