找工作准备刷题day4 二叉树+动态规划

第一题 Leetcode110.平衡二叉树

题解

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

    int getHeight(TreeNode* root) {
        if (root == nullptr)
            return 0;
        int HL = getHeight(root->left);
        if (HL == -1)
            return -1;
        int HR = getHeight(root->right);
        if (HR == -1)
            return -1;
        return abs(HR - HL) > 1 ? -1 : 1 + max(HR, HL);
    }
};



第二题 Leetcode257. 二叉树的所有路径

题解

class Solution {
public:
    vector<string> ans;

    vector<string> binaryTreePaths(TreeNode* root) {
        if (root == nullptr)
            return ans;
        string str = "";
        bT(root, str, 0);

        return ans;
    }

    void bT(TreeNode* root, string str, int time) {
        if (time > 0)
            str += "->";
        str += to_string(root->val);
        if (root->left == nullptr && root->right == nullptr)
            ans.push_back(str);
        if (root->left)
            bT(root->left, str,1);
        if (root->right)
            bT(root->right, str,1);
    }
};

要点

  1.   回溯算法,回溯特点:使用函数参数的传值
  2. void bT(TreeNode* root, string str, int time) 
    函数参数含义:str 为已有路径,time 为是否是第一个数字,用于控制 “->” 的输出
  3. to_string() 函数将数字转成字符串,位于<string>头文件中;atoi 函数将字符串转成整数,如果不能转换则返回0,位于 <stdlib.h>,对于string字符串,需要使用 str.c_str() 函数将其转成char*atof 类似。



第三题:Leetcode404.左叶子之和

题解

class Solution {
public:
    int sumOfLeftLeaves(TreeNode* root) {
        if (root == nullptr ||
            (root->left == nullptr && root->right == nullptr))
            return 0;

        int leftV = sumOfLeftLeaves(root->left);
        if (root->left != nullptr && root->left->left == nullptr &&
            root->left->right == nullptr)
            // 左子树就是左叶子,这时候leftV取值必定为0
            leftV = root->left->val;

        int rightV = sumOfLeftLeaves(root->right);
        // cout << leftV << endl << rightV << endl;
        return rightV + leftV;
    }
};

要点

  1.  左叶子定义:节点A的左孩子不为空,且左孩子的左右孩子都为空(说明是叶子节点),那么A节点的左孩子为左叶子节点。
  2. 当遇到左叶子节点的时候,记录数值,然后通过递归求取左子树左叶子之和,和 右子树左叶子之和,相加便是整个树的左叶子之和。

 疑惑

如果将​​​​

if (root == nullptr || (root->left == nullptr && root->right == nullptr))
    return 0;

换成

if (root == nullptr)
   return 0;
if (root->left == nullptr && root->right == nullptr)
   return 0;

运行时间就会由 0ms 提高到 9ms,影响有这么大?

第四题:Leetcode222. 完全二叉树的节点个数

题解

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


第五题:Leetcode746. 使用最小花费爬楼梯——动态规划

题目描述

思路

动态规划的要点

 1. 确定dp数组(dp table)以及下标的含义
 2. 确定递推公式
 3. dp数组如何初始化
 4. 确定遍历顺序
 5. 举例推导dp数组

动态规划题目牢牢抓紧这五个步骤解题。

  1.  本题中,dp 数组被初始化为 n+1 大小的int数组,最后返回 dp[n] 元素,dp[i] 表示到达下标为i的台阶的最低花费。
  2. 初始化:dp[0] = dp[1] = 0 ,表示下标为0和下标为1的台阶代价为0。
  3. 递推公式:要到达下标为 i 的台阶,可以在 i-1 花费代价 cost[i-1] 到达,也可以在 i-2 花费代价cost[i-2] 到达,因此递归公式为 dp[i] = max( dp[i-1] + cost[i-1] , dp[i-2] + cost[i-2] ),从而遍历顺序为从低到高。
  4. 优化:由于只使用 dp[i-1] dp[i-2] ,因此使用两个变量代替dp数组即可,将空间复杂度降到O(1),时间复杂度仍然是O(n)。

 题解

class Solution {
public:
    int minCostClimbingStairs(vector<int>& cost) {
        int dp0 = 0, dp1 = 0;
        int dpi;
        for (int i = 2; i <= cost.size(); i++) {
            dpi = min(dp0 + cost[i - 2], dp1 + cost[i - 1]);
            dp0 = dp1;
            dp1 = dpi;
        }
        return dp1;
    }
};

第六题:Leetcode62. 不同路径

题目描述

题解1——数论组合

总共需要走m+n-2步,其中m-1步向下,n-1步向右,因此是\textrm{c}_{m+n-2}^{m-1}=\frac{(m+n-2)!}{(m-1)!(n-1)!} = \frac{n(n+1)...(m+n-2)}{(m-1)!} = \frac{m(m+1)...(m+n-2)}{(n-1)!}

如果使用连乘算出来分子,int会溢出,因此需要一边乘,一边除。

class Solution {
public:
    int uniquePaths(int m, int n) {
        long long fenzi=1;
        int fenmu = m - 1;
        int count = m - 1;
        int t = n + m - 2;
        while (count-- > 0) {
            fenzi *= t;
            while (fenmu != 0 && fenzi % fenmu == 0) {
                fenzi /= fenmu;
                fenmu--;
            }
            cout<<"分子;"<<fenzi <<endl;
            t--;
        }
        return fenzi;
    }
};

题解2——动态规划

动态规划的要点

 1. 确定dp数组(dp table)以及下标的含义
 2. 确定递推公式
 3. dp数组如何初始化
 4. 确定遍历顺序
 5. 举例推导dp数组

动态规划题目牢牢抓紧这五个步骤解题。

  1. dp数组为m\times ndp[i][j]表示从(0,0)(i,j)的不同路径数。
  2. 对于dp[i][j],可以从(i-1,j)下移一步或者从(i,j-1)右移一步,因此递推公式为dp[i][j] = dp[i-1][j] + dp[i][j-1]
  3. 初始化:dp[i][0] = dp[0][j] = 1
  4. i 从1遍历到m-1j1遍历到n-1,返回dp[m-1][n-1]
class Solution {
public:
    int uniquePaths(int m, int n) {
        vector<vector<int>> dp(m, vector<int>(n, 1));
        for (int i = 1; i < m; i++) {
            for (int j = 1; j < n; j++) {
                dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
            }
        }
       

        return dp[m - 1][n - 1];
    }
};

第七题:Leetcode63. 不同路径 II

题目描述

思路

与62题类似,仅仅修改一下初始化和递推逻辑。

  1. dp[i][0]dp[0][j]在遇到obstacle之前初始化为1,在遇到obstacle之后(包括obstacle)初始化为0。
  2. (i,j)为obstacle时,dp[i][j] = 0,否则递推公式为:  dp[i][j] = dp[i-1][j] + dp[i][j-1]

题解

class Solution {
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
        const int m = obstacleGrid.size();
        const int n = obstacleGrid[0].size();
        vector<vector<int>> dp(m, vector<int>(n, 0));
        for (int i = 0; i < m; i++) {
            if (obstacleGrid[i][0] == 1)
                break;
            dp[i][0] = 1;
        }
        for (int j = 0; j < n; j++) {
            if (obstacleGrid[0][j] == 1)
                break;
            dp[0][j] = 1;
        }

        for (int i = 1; i < m; i++)
            for (int j = 1; j < n; j++)
                if (obstacleGrid[i][j] == 1)
                    dp[i][j] = 0;
                else {
                    dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
                }

        return dp[m - 1][n - 1];
    }
};

第八题:Leetcode343. 整数拆分

题目描述

思路

  1. 递推数组dp[n+1]dp[i]表示数字i拆分的最大值。
  2. 对于\forall i>2,遍历j \subseteq \left \{ 1,2,...,i-1 \right \},在元素j进行拆分,dp[i] = max( (i-j)*j , dp[i-j]*j),第一项(i-j)*j代表将i拆分为ji-j两个数字,第二项代表将i拆分成j、并且将i-j也拆分成多项。
  3. dp[2]初始化为1
  4. i从3开始遍历到n,j从1遍历到n-1。
  5. 优化:对于\forall i>2,根据常识,将其拆分为最接近的两个数,乘积会更大一些,因此j遍历区间改为[1,i/2]。

题解

class Solution {
public:
    int integerBreak(int n) {
        vector<int> dp(n + 1, 0);
        dp[2] = 1;
        for (int i = 3; i <= n; i++) {
            for (int j = 1; j <= i / 2; j++) {
                //  max(j * (i - j), j * dp[i - j]),前者是将i拆解成j和i-j两个数
                // 后者是将i拆解成 j和(i-j拆解的结果)
                // 第一个dp[i]是不同的j的结果
                dp[i] = max(dp[i], max(j * (i - j), j * dp[i - j]));
            }
        }

        return dp[n];
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值