月度归档: 2021年9月

LeetCode刷题【1218】最长定差子序列

给你一个整数数组 arr 和一个整数 difference,请你找出并返回 arr 中最长等差子序列的长度,该子序列中相邻元素之间的差等于 difference

子序列 是指在不改变其余元素顺序的情况下,通过删除一些元素或不删除任何元素而从 arr 派生出来的序列。

 

示例 1:

输入:arr = [1,2,3,4], difference = 1
输出:4
解释:最长的等差子序列是 [1,2,3,4]。

示例 2:

输入:arr = [1,3,5,7], difference = 1
输出:1
解释:最长的等差子序列是任意单个元素。

示例 3:

输入:arr = [1,5,7,8,5,3,4,2,1], difference = -2
输出:4
解释:最长的等差子序列是 [7,5,3,1]。

 

提示:

  • 1 <= arr.length <= 105
  • -104 <= arr[i], difference <= 104
Related Topics
  • 数组
  • 哈希表
  • 动态规划

  • 👍 73
  • 👎 0
  • 上代码

    class Solution {
        public int longestSubsequence(int[] arr, int difference) {
            HashMap<Integer,Integer> map = new HashMap<>();
            int[] dp = new int[arr.length];
            dp[0]=1;
            map.put(arr[0],1);
            int i = 1;
            for (; i < arr.length; i++) {
                dp[i] = Math.max(dp[i-1], map.getOrDefault(arr[i]-difference,0)+1 );
                map.put(arr[i],map.getOrDefault(arr[i]-difference,0)+1);
            }
            return dp[--i];
        }
    }

    思路简析

    建立对应长度DP数组,第i位置表示最长等差子序列的长度,详细思路的写在注解里

    class Solution {
        public int longestSubsequence(int[] arr, int difference) {
            HashMap<Integer,Integer> map = new HashMap<>();
            int[] dp = new int[arr.length];
            //第一位的时候显然最长一定为1
            dp[0]=1;
            //表示以arr[0]为结尾的等差数列子序列的长度为1
            map.put(arr[0],1);
            int i = 1;
            for (; i < arr.length; i++) {
                //第i位的时候。
                //此时也许当前值减去difference的值,在map中有记录存在有这个值结尾的等差数列,
                //也可能这个等差数列的长度比之前[i-1]位的等差数列的长度要小,
                //俩着取较大的一个
                dp[i] = Math.max(dp[i-1], map.getOrDefault(arr[i]-difference,0)+1 );
                //在map中记录或者更新更新以当前值结尾的等差数列子序列的最大长度
                map.put(arr[i],map.getOrDefault(arr[i]-difference,0)+1);
            }
            return dp[--i];
        }
    }

    换个角度

    上面的写法中使用HashMap的原因是key值可能为负数,如果是正数int型的key的话一般还有一种做法是直接用int[]数组代替hashmap来处理相关逻辑,比封装后的HashMap更加简洁高效,当时相对的也有更多局限性。

    本题中给出了-10⁴ <= arr[i], difference <= 10⁴


    那么arr[i]的范围就在-9999到9999之间,如果再加上需要和difference运算的话,那么应该是-20000 < arr[i] +/- difference < 20000 之间,那么对应的上面的int[]数组代替hashmap的更加高效的选择的作法的话,就变成如下的代码

    class Solution {
        public int longestSubsequence(int[] arr, int difference) {
            int[] map = new int[40001];
            int[] dp = new int[arr.length];
            for (int i = 0; i < arr.length; i++) {
                arr[i] = arr[i]+20000;
            }
            dp[0]=1;
            map[arr[0]] = 1;
            int i = 1;
            for (; i < arr.length; i++) {
                dp[i] = Math.max(dp[i-1], map[arr[i]-difference]+1 );
                map[arr[i]]= map[arr[i]-difference]+1;
            }
            return dp[--i];
        }
    }
    

    LeetCode刷题【1137】第 N 个泰波那契数

    泰波那契序列 Tn 定义如下: 

    T0 = 0, T1 = 1, T2 = 1, 且在 n >= 0 的条件下 Tn+3 = Tn + Tn+1 + Tn+2

    给你整数 n,请返回第 n 个泰波那契数 Tn 的值。

     

    示例 1:

    输入:n = 4
    输出:4
    解释:
    T_3 = 0 + 1 + 1 = 2
    T_4 = 1 + 1 + 2 = 4
    

    示例 2:

    输入:n = 25
    输出:1389537
    

     

    提示:

    • 0 <= n <= 37
    • 答案保证是一个 32 位整数,即 answer <= 2^31 - 1
    Related Topics
  • 记忆化搜索
  • 数学
  • 动态规划

  • 👍 137
  • 👎 0
  • 一样动态规划,同LeetCode刷题【509】斐波那契数

    class Solution {
        public int tribonacci(int n) {
            if(n<2){
                return n;
            }
            int[] dp = new int[n+1];
            dp[1] = 1;
            dp[2] = 1;
            int i = 2;
            while(++i <=n){
                dp[i] = dp[i-1] + dp[i-2] + dp[i-3];
            }
            return dp[n];
        }
    }

    LeetCode刷题【509】斐波那契数

    斐波那契数,通常用 F(n) 表示,形成的序列称为 斐波那契数列 。该数列由 01 开始,后面的每一项数字都是前面两项数字的和。也就是:

    F(0) = 0,F(1) = 1
    F(n) = F(n - 1) + F(n - 2),其中 n > 1
    

    给你 n ,请计算 F(n)

     

    示例 1:

    输入:2
    输出:1
    解释:F(2) = F(1) + F(0) = 1 + 0 = 1
    

    示例 2:

    输入:3
    输出:2
    解释:F(3) = F(2) + F(1) = 1 + 1 = 2
    

    示例 3:

    输入:4
    输出:3
    解释:F(4) = F(3) + F(2) = 2 + 1 = 3
    

     

    提示:

    • 0 <= n <= 30
    Related Topics
  • 递归
  • 记忆化搜索
  • 数学
  • 动态规划

  • 👍 327
  • 👎 0
  • 动态规划,

    直接上代码了,没啥太多的了,

    class Solution {
        public int fib(int n) {
            if(n==0){
                return 0;
            }
            int[] dp = new int[n+1];
            dp[0] = 0;
            dp[1] = 1;
            int i = 1;
            while(++i <= n){
                dp[i] = dp[i-1] + dp[i-2];
            }
            return dp[n];
        }
    }

    LeetCode刷题【223】矩形面积

    给你 二维 平面上两个 由直线构成的 矩形,请你计算并返回两个矩形覆盖的总面积。

    每个矩形由其 左下 顶点和 右上 顶点坐标表示:

    • 第一个矩形由其左下顶点 (ax1, ay1) 和右上顶点 (ax2, ay2) 定义。
    • 第二个矩形由其左下顶点 (bx1, by1) 和右上顶点 (bx2, by2) 定义。

     

    示例 1:

    输入:ax1 = -3, ay1 = 0, ax2 = 3, ay2 = 4, bx1 = 0, by1 = -1, bx2 = 9, by2 = 2
    输出:45
    

    示例 2:

    输入:ax1 = -2, ay1 = -2, ax2 = 2, ay2 = 2, bx1 = -2, by1 = -2, bx2 = 2, by2 = 2
    输出:16
    

     

    提示:

    • -104 <= ax1, ay1, ax2, ay2, bx1, by1, bx2, by2 <= 104
    Related Topics
  • 几何
  • 数学

  • 👍 135
  • 👎 0
  • class Solution {
        public int computeArea(int ax1, int ay1, int ax2, int ay2, int bx1, int by1, int bx2, int by2) {
            int cx1 = Math.max(ax1,bx1);
            int cy1 = Math.max(ay1,by1);
            int cx2 = Math.min(ax2,bx2);
            int cy2 = Math.min(ay2,by2);
            int overlappingArea = 0;
            if (cx1 < cx2 && cy1 < cy2){
                overlappingArea = areaOf( cx1, cy1, cx2, cy2);
            }
    
            return areaOf( ax1, ay1, ax2, ay2) + areaOf( bx1, by1, bx2, by2) - overlappingArea;
    
        }
    
        public int areaOf(int x1, int y1, int x2, int y2){
            return ( x2 - x1 ) * ( y2 - y1);
        }
    }

    本题题面中有一个重要的信息
    每个矩形由其 左下 顶点和 右上 顶点坐标表示

    所以我们需要找出重叠部分对应的左下角和右上角。
    由图中可见,C1和C2点相应的关系也就可以基本明晰了
    而特殊情况下,如果两个矩形没有重叠部分,则求出来的C1不能保证在C2点的左下方

    LeetCode刷题【113】路径总和 II

    给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。

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

     

    示例 1:

    输入:root = [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum = 22
    输出:[[5,4,11,2],[5,8,4,5]]
    

    示例 2:

    输入:root = [1,2,3], targetSum = 5
    输出:[]
    

    示例 3:

    输入:root = [1,2], targetSum = 0
    输出:[]
    

     

    提示:

    • 树中节点总数在范围 [0, 5000]
    • -1000 <= Node.val <= 1000
    • -1000 <= targetSum <= 1000
    Related Topics
  • 深度优先搜索
  • 回溯
  • 二叉树

  • 👍 586
  • 👎 0
  • 简单

    就DFS,在叶子节点的时候,即没有左右子节点的时候,判断这个链路上的和是否等于targetSum,如果等于则把之前记录的path加入到list集合中,此处需要复制一份出来。
    并再退出的时候重新减去当前node的val,

    class Solution {
        List<List<Integer>> list = new ArrayList<>();
        int sum = 0;
        int targetSum = 0;
        public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
            this.targetSum = targetSum;
            dfs(root,new ArrayList<>());
            return list;
        }
    
        public void dfs(TreeNode node, List<Integer> path){
            if (node==null){
                return;
            }
            sum += node.val;
            path.add(node.val);
            if (node.left == null && node.right == null && sum == targetSum){
                list.add(new ArrayList<>(path));
            }
            dfs(node.left, path);
            dfs(node.right, path);
            path.remove(path.size()-1);
            sum -=node.val;
        }
    }
    

    LeetCode刷题【518】零钱兑换 II

    给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额。

    请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0

    假设每一种面额的硬币有无限个。 

    题目数据保证结果符合 32 位带符号整数。

     

    示例 1:

    输入:amount = 5, coins = [1, 2, 5]
    输出:4
    解释:有四种方式可以凑成总金额:
    5=5
    5=2+2+1
    5=2+1+1+1
    5=1+1+1+1+1
    

    示例 2:

    输入:amount = 3, coins = [2]
    输出:0
    解释:只用面额 2 的硬币不能凑成总金额 3 。
    

    示例 3:

    输入:amount = 10, coins = [10] 
    输出:1
    

     

    提示:

    • 1 <= coins.length <= 300
    • 1 <= coins[i] <= 5000
    • coins 中的所有值 互不相同
    • 0 <= amount <= 5000
    Related Topics
  • 数组
  • 动态规划

  • 👍 617
  • 👎 0
  • 以需要凑出0到amount金额来处理,已有零钱面值为[1,2,5],
    热知识:现有的货币面值单位就是[1,2,5],这是一个非常高效的组合,具体更多原因和背后考量可以自行深入研究下

    已知0元的情况,直接不用任何面值可以记为1种情况。

    • 当我们有了面值为1的硬币之后,
      • 如果一定要用面值为1的硬币的话,显然凑不出0元,只能使用原来只有0元的时候的情况
      • 而如果面值大于0了,可以用当前面值减去面值为1的情况加一个硬币得来
    • 当我们有了面值为2的硬币之后,
      • 如果想要凑出面值小于2的情况,显然是不行的,只能用之前的方案代替
      • 而大于等于2的时候,可以选择 前面的当前面值减去2元的情况加上一枚2元的硬币来组成 或者 之前没有2元面值的时候凑出2元的方案
    • 到这里逻辑和转化方程就都已经清楚了f [i][j] = f [i − 1][j] + f [i][j − x]
    • 最后拿总额11来再说明下
      • 可以是之前没有5元面值的时候凑出11元的方案
      • 也可以是需要有5元面值硬币的时候凑出6元的方案加一个5元硬币.

    代码

    我们把越界索引为负数的部分当做0处理

    class Solution {
        public int change(int amount, int[] coins) {
            int [][] dp = new int[coins.length+1][amount+1];
            dp[0][0] = 1;
            for (int row = 1; row <= coins.length; row++) {
                for (int col = 0; col <= amount; col++) {
                    dp[row][col] = dp[row-1][col] + (col>=coins[row-1]?dp[row][col-coins[row-1]]:0);
                }
            }
            return dp[coins.length][amount];
        }
    }

    再思考下

    每多出来一种面值硬币的可选项之后,都是在之前已得到方案的结果上进行叠加处理,那么可以只声明一个长度为amount+1的数组,在每多加一种面值硬币的时候就对结果进行重新叠加统计处理。而小于新加的硬币的面值的总额不必处理,和前面说到的情况一样。
    大概如图,可能有点不是太清晰,中间省略了几步

    1. 预定义了dp[0] = 1
    2. 从面值为1的硬币开始遍历处理,当面值0的情况加1枚1元硬币,可以得到总额1,后面挨个加可以得到[1,1,1,1,1,1,1,1,1,1,1,1]的结果
    3. 新加入了面值2,在原来的基础上,面值为0的情况加1枚硬币2可以得到总额为2的情况1种,累加上原来面值为2的情况1种,此时可以有2中情况可以凑出面值2,后面依次遍历处理
    4. 中间省略
    5. 最后新加了面值5,依旧从面值0开始加一个5元面值硬币则代表一种新的凑出5元的方案,加上原来的方案数量3种,此时可知有4种方法凑出5元
    6. 最后的11元等于之前的6种情况加上使用5元凑出6元的6种方案,一共11种方案
    class Solution {
        public int change(int amount, int[] coins) {
            int []dp = new int[amount+1];
            dp[0] = 1;
            for (int coin : coins) {
                for (int i = coin; i <= amount; i++) {
                    dp[i] += dp[i-coin];
                }
            }
            return dp[amount];
        }
    }
    

    LeetCode刷题【455】分发饼干

    假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。

    对每个孩子 i,都有一个胃口值 g[i]这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j,都有一个尺寸 s[j] 。如果 s[j] >= g[i],我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。

     

    示例 1:

    输入: g = [1,2,3], s = [1,1]
    输出: 1
    解释: 
    你有三个孩子和两块小饼干,3个孩子的胃口值分别是:1,2,3。
    虽然你有两块小饼干,由于他们的尺寸都是1,你只能让胃口值是1的孩子满足。
    所以你应该输出1。
    

    示例 2:

    输入: g = [1,2], s = [1,2,3]
    输出: 2
    解释: 
    你有两个孩子和三块小饼干,2个孩子的胃口值分别是1,2。
    你拥有的饼干数量和尺寸都足以让所有孩子满足。
    所以你应该输出2.
    

     

    提示:

    • 1 <= g.length <= 3 * 104
    • 0 <= s.length <= 3 * 104
    • 1 <= g[i], s[j] <= 231 - 1
    Related Topics
  • 贪心
  • 数组
  • 排序

  • 👍 382
  • 👎 0
  • 每一个孩子只给予能满足胃口的尽量小的数量,照着这个思路所以需要把孩子按照胃口量大小从小到大排序,饼干数量也按照从小到大排序

    • 为了满足某一个孩子,则从饼干数量的数组中按照从小到大的顺序尝试给予,给到能给予他的最小的量
    • 下一个孩子的胃口则必定是大于当前孩子的,此时饼干数量也可以从之前取到的顺序继续往后查找到能满足他的最小的量

    代码

    class Solution {
        public int findContentChildren(int[] g, int[] s) {
            int count = 0;
            Arrays.sort(g);
            Arrays.sort(s);
            int gIdx = 0;
            int sIdx = 0;
            while(gIdx < g.length){
                while( sIdx < s.length){
                    if(s[sIdx]>= g[gIdx]){
                        count++;
                        sIdx++;
                        break;
                    }
                    sIdx++;
                }
                gIdx++;
            }
    
    
            return count;
        }
    }