LeetCode刷题笔记(Java)---第181-200题

前言

关于数据库的题目和写脚本的题目略过

笔记导航

点击链接可跳转到所有刷题笔记的导航链接

187. 重复的DNA序列

所有 DNA 都由一系列缩写为 A,C,G 和 T 的核苷酸组成,例如:“ACGAATTCCG”。在研究 DNA 时,识别 DNA 中的重复序列有时会对研究非常有帮助。

编写一个函数来查找 DNA 分子中所有出现超过一次的 10 个字母长的序列(子串)。
在这里插入图片描述

  • 解答
    public List<String> findRepeatedDnaSequences(String s) {
        Set<String> set = new HashSet<>();
        List<String> list = new ArrayList<>();
        for (int i = 0; i < s.length() - 9; i++) {
            String s1 = s.substring(i,i+10);
            if(set.contains(s1) && !list.contains(s1))
                list.add(s1);
            else set.add(s1);
        }
        return list;
    }
  • 分析

    1.窗口大小为10,每次截取字符串s中窗口大小为10的子串,若set集合中存在 ,则表示这个子串重复,list不包含该子串是为了避免添加进去重复的子串。
    2.若set中不存在,则将该子串添加到set集合,表示第一次出现。

  • 提交结果
    在这里插入图片描述

188. 买卖股票的最佳时机 IV

给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你最多可以完成 k 笔交易。

注意: 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

  • 解答
    public static int maxProfit(int k, int[] prices) {
        if (k == 0 || prices.length == 0) return 0;
        int n = prices.length;
        if (k / 2 > n) return minProfit(prices);//若k/2大于n表示,和k为无穷大一个意思。
        int[][][] dp = new int[n][k + 1][2];
        for (int i = 0; i < n; i++) {
            for (int j = k; j >= 1; j--) {
                if (i == 0) {//第一天初始化
                    dp[i][j][0] = 0;
                    dp[i][j][1] = -prices[i];
                    continue;
                }
                //动态转移方程
                dp[i][j][0] = Math.max(dp[i - 1][j][0], dp[i - 1][j][1] + prices[i]);
                dp[i][j][1] = Math.max(dp[i - 1][j][1], dp[i - 1][j - 1][0] - prices[i]);
            }
        }
        return dp[n - 1][k][0];
    }

    public static int minProfit(int[] prices) {
        int n = prices.length;
        int dp_0 = 0;
        int dp_1 = Integer.MIN_VALUE;
        for (int i = 0; i < n; i++) {
            int temp = dp_0;
            dp_0 = Math.max(dp_0, dp_1 + prices[i]);
            dp_1 = Math.max(dp_1, temp - prices[i]);
        }
        return dp_0;
    }
  • 分析

    1.动态规划实现,dp[i][k][j]
    i表示天数 k表示最大交易次数 j表示状态 0为不持有股票 1为持有股票
    2.
    dp[0][k][1] 第一天持有股票 所以初始化为-prices[i]
    dp[0][k][0] 第一天不持有股票初始化为0

    dp[i][k][1] 第i的持有股票的状态是第i-1天的状态转换过来的
    分为两种

      第一种第i-1天也持用股票
      dp[i][k][1] = dp[i-1][k][1]
      第二种第i-1天没有持有股票 说明第i天买入了股票
      dp[i][k][1] = dp[i-1][k-1][0] - prices[i]
      
      选择大的一个作为dp[i][k][1]
    

    dp[i][k][0] 同样分为两种

      第一种第i-1天没有股票
      dp[i][k][0] = dp[i-1][k][0]
      第二种第i-1天持有股票,说明第i天卖出了股票
      dp[i][k][0] = dp[i-1][k][1] + prices[i]
    

    3.若k / 2 > n 题目就和k没关系了。转换成之前做过的交易任意次可获得最大利润

    4.可以将三维数组转换成2维,因为第一维天数,之和前一天有关。
    所以动态转移方程可以改为
    dp[j][0] = Math.max(dp[j][0], dp[j][1] + prices[i]);
    dp[j][1] = Math.max(dp[j][1], dp[j-1][0] - prices[i]);

  • 提交结果
    在这里插入图片描述

189. 旋转数组

给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。

在这里插入图片描述

  • 解答
    public static void rotate(int[] nums, int k) {
        k = k % nums.length;
        if (nums.length < 2 || k == 0) return;
        reverse(nums,0,nums.length-1);
        reverse(nums, 0, k-1);
        reverse(nums, k, nums.length - 1);
    }
    //反转数组
    public static void reverse(int[] nums, int start, int end) {
        while (start < end) {
            int tmp = nums[start];
            nums[start] = nums[end];
            nums[end] = tmp;
            start++;
            end--;
        }
    }
  • 分析

    1.先对数组全体反转
    2.对前k个反转
    3.对剩余的反转
    例如 1,2,3,4,5,6,7 k=2
    先全体反转7,6,5,4,3,2,1
    前k个反转5,6,7,4,3,2,1
    剩余反转5,6,7,1,2,3,4 即为答案

  • 提交结果
    在这里插入图片描述

190. 颠倒二进制位

颠倒给定的 32 位无符号整数的二进制位。
在这里插入图片描述
提示:

请注意,在某些语言(如 Java)中,没有无符号整数类型。在这种情况下,输入和输出都将被指定为有符号整数类型,并且不应影响您的实现,因为无论整数是有符号的还是无符号的,其内部的二进制表示形式都是相同的。
在 Java 中,编译器使用二进制补码记法来表示有符号整数。因此,在上面的 示例 2 中,输入表示有符号整数 -3,输出表示有符号整数 -1073741825。
  • 解答
    public int reverseBits(int n) {
        int power = 31;
        int res = 0;
        for (int i = 0; i <= power; i++) {
            res += (1 & (n >> i))<<(power-i);
        }
        return res;
    }
  • 分析

    1.n>>i 右移动i位,然后和1进行与运算,表示从右往左遍历二进制数
    2.然后得到的结果左移动31-i。表示移动到对应的位置,即反转。

  • 提交结果
    在这里插入图片描述

191. 位1的个数

编写一个函数,输入是一个无符号整数,返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为汉明重量)。
在这里插入图片描述

提示:

请注意,在某些语言(如 Java)中,没有无符号整数类型。在这种情况下,输入和输出都将被指定为有符号整数类型,并且不应影响您的实现,因为无论整数是有符号的还是无符号的,其内部的二进制表示形式都是相同的。
在 Java 中,编译器使用二进制补码记法来表示有符号整数。因此,在上面的 示例 3 中,输入表示有符号整数 -3。
  • 解答
    public int hammingWeight(int n) {
        int sum = 0;
        for (int i = 0; i<=31 ; i++) {
            sum += (1&(n>>i)) == 1 ? 1:0;
        }
        return sum;
    }
  • 分析

    1.和前一题一样从右往左遍历二进制,与1进行与计算,结果位1,表示这一位为1,sum++

  • 提交结果
    在这里插入图片描述

198. 打家劫舍

你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。

给定一个代表每个房屋存放金额的非负整数数组,计算你在不触动警报装置的情况下,能够偷窃到的最高金额。

在这里插入图片描述

  • 解答
    public int rob(int[] nums) {
        int n = nums.length;
        if (n == 0) return 0;
        int[][] dp = new int[n + 1][2];
        dp[0][0] = 0;
        dp[0][1] = 0;
        for (int i = 1; i <= n; i++) {
            dp[i][0] = dp[i - 1][1] + nums[i - 1];//第i个偷
            dp[i][1] = Math.max(dp[i - 1][0], dp[i - 1][1]);//第i个不偷
        }
        return Math.max(dp[n][0],dp[n][1]);
    }
    
    public static int rob2(int[] nums) {
        int n = nums.length;
        if (n == 0) return 0;
        int dp_i_0 = 0;
        int dp_i_1 = 0;
        for (int i = 0; i < n; i++) {
            int tmp = dp_i_1;
            int tmp2 = dp_i_0;
            dp_i_0 = dp_i_1 + nums[i];
            dp_i_1 = Math.max(tmp, tmp2);
        }
        return Math.max(dp_i_0, dp_i_1);
    }
  • 分析

    1.动态规划实现,
    dp[i][0]表示偷窃第i个小屋所能获得的最大金额
    因为第i个偷 所以第i-1个不能偷
    所以dp[i][0] = dp[i - 1][1] + nums[i - 1]

    dp[i][1]表示不偷窃第i个小屋所能获得的最大金额
    所以就返回dp[i-1][1]和dp[i-1][0]中的较大者。
    dp[i][1] = Math.max(dp[i - 1][0], dp[i - 1]

    2.初始化dp[0][0],dp[0][1]均为0
    3.最后返回dp[n][0]和dp[n][1]中的较大者
    4.因为dp[i]之和前一个dp[i-1]有关,所以可以优化一下,不需要保留所有的结果。见方法二

  • 提交结果
    方法一
    在这里插入图片描述
    方法二
    在这里插入图片描述

199. 二叉树的右视图

给定一棵二叉树,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。
在这里插入图片描述

  • 解答
    public static List<Integer> rightSideView(TreeNode root) {
        List<List<Integer>> lists = new ArrayList<>();
        rightsSideView(root,lists,0);
        List<Integer> res = new ArrayList<>();
        for(List<Integer> list : lists){
            res.add(list.get(list.size()-1));
        }
        return res;
    }

    public static void rightsSideView(TreeNode root, List<List<Integer>> lists, int depth) {
        if (root != null) {
            if(lists.size()<=depth)
                lists.add(new ArrayList<>());
            lists.get(depth).add(root.val);
        }else return;
        rightsSideView(root.left,lists,depth+1);
        rightsSideView(root.right,lists,depth+1);
    }
  • 分析

    1.层次遍历,然后取每一层最右边的即可。

  • 提交结果
    在这里插入图片描述

200. 岛屿数量

给你一个由 ‘1’(陆地)和 ‘0’(水)组成的的二维网格,请你计算网格中岛屿的数量。

岛屿总是被水包围,并且每座岛屿只能由水平方向或竖直方向上相邻的陆地连接形成。

此外,你可以假设该网格的四条边均被水包围。
在这里插入图片描述

  • 解答
    public static int numIslands(char[][] grid) {
        int height = grid.length;
        if (height == 0)return 0;
        int width = grid[0].length;
        int res = 0;
        for (int i = 0; i < height; i++) {
            for (int j = 0; j < width; j++) {
                if (grid[i][j] == '1') {
                    numIslands(grid, i, j, height, width);
                    res++;
                }
            }
        }
        return res;
    }
    
    // 深度搜索
    public static void numIslands(char[][] grid, int i, int j, int height, int width) {
        if (i < 0 || i >= height || j < 0 || j >= width || grid[i][j] == '0') return;
        if (grid[i][j] == '1') {
            grid[i][j] = '2';
            numIslands(grid, i + 1, j, height, width);
            numIslands(grid, i - 1, j, height, width);
            numIslands(grid, i, j + 1, height, width);
            numIslands(grid, i, j - 1, height, width);
        }
    }
  • 分析

    1.遍历矩阵,当遇到岛屿,即遇到字符’1’。则深度搜索,找到相连的岛屿,改为字符’2’。res计数+1,表示找到一个岛屿
    2.最后返回res即可

  • 提交结果
    在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/gongsenlin341/article/details/106156320