Java实现LeetCode 题号:491 - 500

117 阅读6分钟

这是我参与11月更文挑战的第30天,活动详情查看:2021最后一次更文挑战

LeetCode习题集 有些题可能直接略过了,整理一下之前刷leetcode

491. 递增子序列

给定一个整型数组, 你的任务是找到所有该数组的递增子序列,递增子序列的长度至少是2。

示例:

输入: [4, 6, 7, 7] 输出: [[4, 6], [4, 7], [4, 6, 7], [4, 6, 7, 7], [6, 7], [6, 7, 7], [7,7], [4,7,7]] 说明:

给定数组的长度不会超过15。 数组中的整数范围是 [-100,100]。 给定数组中可能包含重复数字,相等的数字应该被视为递增的一种情况。

DFS
    深度搜索每一种可能的情况
    每次搜索的时候,
        如果数量大于1,就创建一个新的集合
        循环每一个数组元素,如果小于上次最小的那个值,就continue,
            因为要的是递增的序列,不能出现比上次小的值
        在本次搜索中,如果有相同的直接进行下一个,否则相同的搜索和前面相同值搜索出来的一样
            这里的相同是指相同位置上的元素相同
         
         然后就是把当前元素加入到集合里面,继续去搜索,
         搜索结束后,把当前的值从集合中删除

class Solution {
    public List<List<Integer>> findSubsequences(int[] nums) {
        List<List<Integer>> ans = new ArrayList<>();
        dfs(nums, new ArrayList<>(), ans, 0, -101);
        return ans;
    }

    private void dfs(int[] nums, List<Integer> list, List<List<Integer>> ans, int index, int lastNum) {
        if (list.size() > 1) {
            ans.add(new ArrayList<>(list));
        }

        for (int i = index; i < nums.length; i++) {
            if (nums[i] < lastNum) {
                continue;
            }

            boolean repeat = false;
            for (int j = index; j <= i - 1; j++) {
                if (nums[i] == nums[j]) {
                    repeat = true;
                    break;
                }
            }

            if (repeat) {
                continue;
            }

            list.add(nums[i]);
            dfs(nums, list, ans, i + 1, nums[i]);
            list.remove(list.size() - 1);
        }
    }
}

492. 构造矩形

作为一位web开发者, 懂得怎样去规划一个页面的尺寸是很重要的。 现给定一个具体的矩形页面面积,你的任务是设计一个长度为 L 和宽度为 W 且满足以下要求的矩形的页面。要求:

  1. 你设计的矩形页面必须等于给定的目标面积。

  2. 宽度 W 不应大于长度 L,换言之,要求 L >= W 。

  3. 长度 L 和宽度 W 之间的差距应当尽可能小。 你需要按顺序输出你设计的页面的长度 L 和宽度 W。

示例:

输入: 4 输出: [2, 2] 解释: 目标面积是 4, 所有可能的构造方案有 [1,4], [2,2], [4,1]。 但是根据要求2,[1,4] 不符合要求; 根据要求3,[2,2] 比 [4,1] 更能符合要求. 所以输出长度 L 为 2, 宽度 W 为 2。 说明:

给定的面积不大于 10,000,000 且为正整数。 你设计的页面的长度和宽度必须都是正整数。

因为长款的差距要尽可能的小,那么一定是尽量接近正方形的,
目标面积的开方就是正方形的情况下的边长
    但是必须是整数,所以sqrt要用int类型接受
    一定要能用面积去整除边长,否则求出来的长肯定不是整数
class Solution {
      public int[] constructRectangle(int area) {
        int sqrt=(int)Math.sqrt(area); 
        while( area%sqrt!=0 ){ 
            sqrt--;
        }
        return new int[]{area/sqrt,sqrt};
    }
}

493. 翻转对

给定一个数组 nums ,如果 i < j 且 nums[i] > 2*nums[j] 我们就将 (i, j) 称作一个重要翻转对。

你需要返回给定数组中的重要翻转对的数量。

示例 1:

输入: [1,3,2,3,1] 输出: 2 示例 2:

输入: [2,4,3,5,1] 输出: 3 注意:

给定数组的长度不会超过50000。 输入数组中的所有数字都在32位整数的表示范围内。

	二分排序
class Solution {
      private int cnt;

    public int reversePairs(int[] nums) {
        int len = nums.length;
        sort(nums, Arrays.copyOf(nums, len), 0, len - 1);
        return cnt;
    }

    private void sort(int[] src, int[] dest, int s, int e) {
        if (s >= e) {
            return;
        }
        int mid = (s + e) >> 1;
        sort(dest, src, s, mid);
        sort(dest, src, mid + 1, e);
        merge(src, dest, s, mid, e);
    }

    private void merge(int[] src, int[] dest, int s, int mid, int e) {
        int i = s, j = mid + 1, k = s;
        while (i <= mid && j <= e) {
            if ((long) src[i] > 2 * ((long) src[j])) {
                cnt += mid - i + 1;
                j++;
            } else {
                i++;
            }
        }
        i = s;
        j = mid + 1;
        while (i <= mid && j <= e) {
            if (src[i] <= src[j]) {
                dest[k++] = src[i++];
            } else {
                dest[k++] = src[j++];
            }
        }
        while (i <= mid) {
            dest[k++] = src[i++];
        }
        while (j <= e) {
            dest[k++] = src[j++];
        }
    }
}

494. 目标和

给定一个非负整数数组,a1, a2, ..., an, 和一个目标数,S。现在你有两个符号 + 和 -。对于数组中的任意一个整数,你都可以从 + 或 -中选择一个符号添加在前面。

返回可以使最终数组和为目标数 S 的所有添加符号的方法数。

示例 1:

输入: nums: [1, 1, 1, 1, 1], S: 3 输出: 5 解释:

-1+1+1+1+1 = 3 +1-1+1+1+1 = 3 +1+1-1+1+1 = 3 +1+1+1-1+1 = 3 +1+1+1+1-1 = 3

一共有5种方法让最终目标和为3。 注意:

数组非空,且长度不会超过20。 初始的数组的和不会超过1000。 保证返回的最终结果能被32位整数存下。

  494
   输入: nums: [1, 1, 1, 1, 1], S: 3
  输出: 5
  解释:
   -1+1+1+1+1 = 3
   +1-1+1+1+1 = 3
   +1+1-1+1+1 = 3
   +1+1+1-1+1 = 3
  +1+1+1+1-1 = 3
  
  sum(P) 前面符号为+的集合;sum(N) 前面符号为减号的集合
  所以题目可以转化为
 sum(P) - sum(N) = target 
 => sum(nums) + sum(P) - sum(N) = target + sum(nums)
 => 2 * sum(P) = target + sum(nums) 
=> sum(P) = (target + sum(nums)) / 2
因此题目转化为01背包,也就是能组合成容量为sum(P)的方式有多少种
class Solution {

       public static int findTargetSumWays(int[] nums, int S) {
        int sum = 0;
        for (int num : nums) {
            sum += num;
        }
        if (sum < S || (sum + S) % 2 == 1) {
            return 0;
        }
        int w = (sum + S) / 2;
        int[] dp = new int[w + 1];
        dp[0] = 1;
        for (int num : nums) {
            for (int j = w; j >= num; j--) {
                dp[j] += dp[j - num];
            }
        }
        return dp[w];
    }
}

495. 提莫攻击

在《英雄联盟》的世界中,有一个叫 “提莫” 的英雄,他的攻击可以让敌方英雄艾希(编者注:寒冰射手)进入中毒状态。现在,给出提莫对艾希的攻击时间序列和提莫攻击的中毒持续时间,你需要输出艾希的中毒状态总时长。

你可以认为提莫在给定的时间点进行攻击,并立即使艾希处于中毒状态。

示例1:

输入: [1,4], 2 输出: 4 原因: 在第 1 秒开始时,提莫开始对艾希进行攻击并使其立即中毒。中毒状态会维持 2 秒钟,直到第 2 秒钟结束。 在第 4 秒开始时,提莫再次攻击艾希,使得艾希获得另外 2 秒的中毒时间。 所以最终输出 4 秒。 示例2:

输入: [1,2], 2 输出: 3 原因: 在第 1 秒开始时,提莫开始对艾希进行攻击并使其立即中毒。中毒状态会维持 2 秒钟,直到第 2 秒钟结束。 但是在第 2 秒开始时,提莫再次攻击了已经处于中毒状态的艾希。 由于中毒状态不可叠加,提莫在第 2 秒开始时的这次攻击会在第 3 秒钟结束。 所以最终输出 3。 注意:

你可以假定时间序列数组的总长度不超过 10000。 你可以假定提莫攻击时间序列中的数字和提莫攻击的中毒持续时间都是非负整数,并且不超过 10,000,000。

	这个题让我很有动力,
        攻击时间如果大于中毒时间的话,直接加上次的中毒时间就可以,
            否则的话,上次攻击的中毒时间就是本次攻击时间-上次攻击时间
            
            返回的时候看一下算上最后一次攻击
            如果提莫没有攻击,直接返回0

(ง •_•)ง

class Solution {
   public int findPoisonedDuration(int[] timeSeries, int duration) {
        int count = 0;
		for (int i = 1; i < timeSeries.length; i++)
			count += timeSeries[i] - timeSeries[i - 1] > duration ? duration : timeSeries[i] - timeSeries[i - 1];
		return timeSeries.length == 0 ? 0 : count + duration;
    }
}

496. 下一个更大元素 I

给定两个没有重复元素的数组 nums1 和 nums2 ,其中nums1 是 nums2 的子集。找到 nums1 中每个元素在 nums2 中的下一个比其大的值。

nums1 中数字 x 的下一个更大元素是指 x 在 nums2 中对应位置的右边的第一个比 x 大的元素。如果不存在,对应位置输出-1。

示例 1:

输入: nums1 = [4,1,2], nums2 = [1,3,4,2]. 输出: [-1,3,-1] 解释: 对于num1中的数字4,你无法在第二个数组中找到下一个更大的数字,因此输出 -1。 对于num1中的数字1,第二个数组中数字1右边的下一个较大数字是 3。 对于num1中的数字2,第二个数组中没有下一个更大的数字,因此输出 -1。 示例 2:

输入: nums1 = [2,4], nums2 = [1,2,3,4]. 输出: [3,-1] 解释: 对于num1中的数字2,第二个数组中的下一个较大数字是3。 对于num1中的数字4,第二个数组中没有下一个更大的数字,因此输出 -1。 注意:

nums1和nums2中所有元素是唯一的。 nums1和nums2 的数组大小都不超过1000。

	栈记录前面的,map存放数
class Solution {
      public int[] nextGreaterElement(int[] nums1, int[] nums2) {
        Stack<Integer> stack = new Stack<Integer>();
        HashMap<Integer, Integer> hasMap = new HashMap<Integer, Integer>();
        
        int[] result = new int[nums1.length];
        
        for(int num : nums2) {
            while(!stack.isEmpty() && stack.peek()<num){
                hasMap.put(stack.pop(), num);
            }
            stack.push(num);
        }
        
        for(int i = 0; i < nums1.length; i++) result[i] = hasMap.getOrDefault(nums1[i], -1);
            
        return result;
    }
}

498. 对角线遍历

给定一个含有 M x N 个元素的矩阵(M 行,N 列),请以对角线遍历的顺序返回这个矩阵中的所有元素,对角线遍历如下图所示。

示例:

输入: [ [ 1, 2, 3 ], [ 4, 5, 6 ], [ 7, 8, 9 ] ]

输出: [1,2,4,7,5,3,6,8,9]

解释:

在这里插入图片描述

说明:

给定矩阵中的元素总数不会超过 100000 。

	对角线的特点就是x+y相等
class Solution {
   public int[] findDiagonalOrder(int[][] matrix) {
    if (matrix == null || matrix.length == 0) {
        return new int[]{};
    }
    int r = 0, c = 0;
    int row = matrix.length, col = matrix[0].length;
    int[] res = new int[row * col];
    for (int i = 0; i < res.length; i++) {
        res[i] = matrix[r][c];
        // r + c 即为遍历的层数,偶数向上遍历,奇数向下遍历
        if ((r + c) % 2 == 0) {
            if (c == col - 1) {
                // 往右移动一格准备向下遍历
                r++;
            } else if (r == 0) {
                // 往下移动一格准备向下遍历
                c++;
            } else {
                // 往上移动
                r--;
                c++;
            }
        } else {
            if (r == row - 1) {
                // 往右移动一格准备向上遍历
                c++;
            } else if (c == 0) {
                // 往上移动一格准备向上遍历
                r++;
            } else {
                // 往下移动
                r++;
                c--;
            }
        }
    }
    return res;
}
}

500. 键盘行

给定一个单词列表,只返回可以使用在键盘同一行的字母打印出来的单词。键盘如下图所示。

在这里插入图片描述

示例:

输入: ["Hello", "Alaska", "Dad", "Peace"] 输出: ["Alaska", "Dad"]

注意:

你可以重复使用键盘上同一字符。 你可以假设输入的字符串将只包含字母。

因为只允许使用同一行的字母
我们把每一行的字母拿出来放到一个字符串数组里面

循环遍历每一个字符串
    遍历当前字符串的所有字符,
        如果在当前行的字符串找不到,说明当前单词不符合,就去找下一个字符串
class Solution {
     public String[] findWords(String[] words) 
    {
        String[] rec = new String[3];
        rec[0] = "qwertyuiop";
        rec[1] = "asdfghjkl";
        rec[2] = "zxcvbnm";
        

        List<String> list = new ArrayList<>();
 
        for(String str : words)
        {
            for(int j = 0; j < 3; j++)
            {
                boolean flag = true;
 
                for(int k = 0; k < str.length(); k++)
                {
                    char ch = str.charAt(k);

                    //转换成小写
                    if(ch >= 'A' && ch <= 'Z')
                        ch = (char)(ch+32);
                    
                    //在这一行中找不到这个单词,换下一行
                    if(rec[j].indexOf(ch) == -1)
                    {
                        flag = false;
                        break;
                    }
                }
 
                if(flag == true)
                {
                    list.add(str);
                    break;
                }  
            }
        }   
        return list.toArray(new String[]{});

    }
}