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

451. 根据字符出现频率排序

给定一个字符串,请将字符串里的字符按照出现的频率降序排列。

示例 1:

输入: "tree"

输出: "eert"

解释: 'e'出现两次,'r'和't'都只出现一次。 因此'e'必须出现在'r'和't'之前。此外,"eetr"也是一个有效的答案。 示例 2:

输入: "cccaaa"

输出: "cccaaa"

解释: 'c'和'a'都出现三次。此外,"aaaccc"也是有效的答案。 注意"cacaca"是不正确的,因为相同的字母必须放在一起。 示例 3:

输入: "Aabb"

输出: "bbAa"

解释: 此外,"bbaA"也是一个有效的答案,但"Aabb"是不正确的。 注意'A'和'a'被认为是两种不同的字符。

统计每个字符出现的次数
    getChar方法就是找到出现次数最多的字符
    然后把数显次数最多的字符输出相对应的次数,输出后当前字符出现的次数为0
    继续下一次循环,找下一个出现次数最大的字符
class Solution {
       public String frequencySort(String s) {
        int[] map = new int[128];
        for (char ch : s.toCharArray()) {
            map[ch]++;
        }
        int target = -1;
        StringBuilder ans = new StringBuilder();
        while ((target = getChar(map)) > 0) {
            while (map[target]-- > 0) {
                ans.append((char) target);
            }
        }
        return ans.toString();
    }
    
    private int getChar(int[] counting) {
        int idx = -1, max = 0;
        for (int i = 0; i < 128; i++) {
            if (counting[i] > max) {
                max = counting[idx = i];
            }
        }
        return idx;
    }
}

452. 用最少数量的箭引爆气球

在二维空间中有许多球形的气球。对于每个气球,提供的输入是水平方向上,气球直径的开始和结束坐标。由于它是水平的,所以y坐标并不重要,因此只要知道开始和结束的x坐标就足够了。开始坐标总是小于结束坐标。平面内最多存在104个气球。

一支弓箭可以沿着x轴从不同点完全垂直地射出。在坐标x处射出一支箭,若有一个气球的直径的开始和结束坐标为 xstart,xend, 且满足 xstart ≤ x ≤ xend,则该气球会被引爆。可以射出的弓箭的数量没有限制。 弓箭一旦被射出之后,可以无限地前进。我们想找到使得所有气球全部被引爆,所需的弓箭的最小数量。

Example:

输入: [[10,16], [2,8], [1,6], [7,12]]

输出: 2

解释: 对于该样例,我们可以在x = 6(射爆[2,8],[1,6]两个气球)和 x = 11(射爆另外两个气球)。

class Solution {
       public int findMinArrowShots(int[][] points) {
        /** 
        贪心法, 每个气球只少需要一支箭, 先按照右端点排序, 然后每次
        从最小的右端点射出一支箭, 去掉被射爆的气球, 重复该过程. 
        **/
        if(points.length < 1) return 0;
        Arrays.sort(points, (a, b) -> (a[1] - b[1]));
        int count = 1;
        int axis = points[0][1];
        
        for(int i = 1; i < points.length; ++i) {
            if(axis < points[i][0]) {
                count++;
                axis = points[i][1];
            }
        }
        
        return count;
    }
}

453. 最小移动次数使数组元素相等

给定一个长度为 n 的非空整数数组,找到让数组所有元素相等的最小移动次数。每次移动可以使 n - 1 个元素增加 1。

示例:

输入: [1,2,3]

输出: 3

解释: 只需要3次移动(注意每次移动会增加两个元素的值):

[1,2,3] => [2,3,3] => [3,4,3] => [4,4,4]

根据题目要求,每次可以把n-1个元素+1 
    换一种思路:我们可以把某个元素-1,其他元素不变,找到能使元素相同的最小步数
我们把所有数组元素的和求出来,然后找到数组中最小的那个元素,
让每一个元素都变成最小的元素,那时的数组和就是min*nums.length
把当前数组和 - min*nums.length   就是要减去多少
class Solution {
       public int minMoves(int[] nums) {
        int moves = 0, min = Integer.MAX_VALUE;
        for (int i = 0; i < nums.length; i++) {
            moves += nums[i];
            min = Math.min(min, nums[i]);
        }
        return moves - min * nums.length;
    }
 
}

454. 四数相加 II

给定四个包含整数的数组列表 A , B , C , D ,计算有多少个元组 (i, j, k, l) ,使得 A[i] + B[j] + C[k] + D[l] = 0。

为了使问题简单化,所有的 A, B, C, D 具有相同的长度 N,且 0 ≤ N ≤ 500 。所有整数的范围在 -228 到 228 - 1 之间,最终结果不会超过 231 - 1 。

例如:

输入: A = [ 1, 2] B = [-2,-1] C = [-1, 2] D = [ 0, 2]

输出: 2

解释: 两个元组如下:

  1. (0, 0, 0, 1) -> A[0] + B[0] + C[0] + D[1] = 1 + (-2) + (-1) + 2 = 0
  2. (1, 1, 0, 0) -> A[1] + B[1] + C[0] + D[0] = 2 + (-1) + (-1) + 0 = 0
求四个数的和为0
    那么我们求A B的所有可能的和
    如果当前和存在了,就加1,如果当前和不存在就保存当前和的数量为1
    
    然后我们再找C D的所有可能和
    这时候就找是否存在C D和向反的数,使CD和向加等于0,
    如果存在的话,就返回CD和的负数的数量,有多少种相对应负数的数量就有多少种组合
class Solution { 
    public int fourSumCount(int[] A, int[] B, int[] C, int[] D) {
        Map<Integer ,Integer> mapAB =new HashMap<>();
        int res =0;

        for(int i =0 ; i<A.length ;i++){
            for(int j =0 ; j<B.length ;j++){
                int key =A[i]+B[j];
                if(mapAB.containsKey(key))
                    mapAB.put(key,mapAB.get(key)+1);
                else mapAB.put(key,1);
            }
        }

        for(int i =0 ; i<C.length ;i++){
            for(int j =0 ; j<D.length ;j++){
                int key =C[i]+D[j];
                if(mapAB.containsKey(0-key)){
                    res += mapAB.get(0-key);
                }
            }
        }
        return res;

    }
}

455. 分发饼干

假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。对每个孩子 i ,都有一个胃口值 gi ,这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j ,都有一个尺寸 sj 。如果 sj >= gi ,我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。

注意:

你可以假设胃口值为正。 一个小朋友最多只能拥有一块饼干。

示例 1:

输入: [1,2,3], [1,1]

输出: 1

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

输入: [1,2], [1,2,3]

输出: 2

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

class Solution {
    //贪心的思想是,用尽量小的饼干去满足小需求的孩子,所以需要进行排序先
    public int findContentChildren(int[] g, int[] s) {
        int child = 0;
        int cookie = 0;
        Arrays.sort(g);  //先将饼干 和 孩子所需大小都进行排序
        Arrays.sort(s);
        while (child < g.length && cookie < s.length ){ //当其中一个遍历就结束
            if (g[child] <= s[cookie]){ //当用当前饼干可以满足当前孩子的需求,可以满足的孩子数量+1
                child++;
            }
            cookie++; // 饼干只可以用一次,因为饼干如果小的话,就是无法满足被抛弃,满足的话就是被用了
        }
        return child; 
    }
}

456. 132模式

给定一个整数序列:a1, a2, ..., an,一个132模式的子序列 ai, aj, ak 被定义为:当 i < j < k 时,ai < ak < aj。设计一个算法,当给定有 n 个数字的序列时,验证这个序列中是否含有132模式的子序列。

注意:n 的值小于15000。

示例1:

输入: [1, 2, 3, 4]

输出: False

解释: 序列中不存在132模式的子序列。 示例 2:

输入: [3, 1, 4, 2]

输出: True

解释: 序列中有 1 个132模式的子序列: [1, 4, 2]. 示例 3:

输入: [-1, 3, 2, 0]

输出: True

解释: 序列中有 3 个132模式的的子序列: [-1, 3, 2], [-1, 3, 0] 和 [-1, 2, 0].

class Solution {
    public boolean find132pattern(int[] nums) {
        int n = nums.length;
        int last = Integer.MIN_VALUE; // 132中的2
        Stack<Integer> sta = new Stack<>();// 用来存储132中的3
        if(nums.length < 3)
            return false;
        for(int i=n-1; i>=0; i--){

            if(nums[i] < last) // 若出现132中的1则返回正确值
                return true;

            // 若当前值大于或等于2则更新2(2为栈中小于当前值的最大元素)
            while(!sta.isEmpty() && sta.peek() < nums[i]){
                last = sta.pop();
            }

            // 将当前值压入栈中
            sta.push(nums[i]);
        }
        return false;
    }
}

457. 环形数组循环

给定一个含有正整数和负整数的环形数组 nums。 如果某个索引中的数 k 为正数,则向前移动 k 个索引。相反,如果是负数 (-k),则向后移动 k 个索引。因为数组是环形的,所以可以假设最后一个元素的下一个元素是第一个元素,而第一个元素的前一个元素是最后一个元素。

确定 nums 中是否存在循环(或周期)。循环必须在相同的索引处开始和结束并且循环长度 > 1。此外,一个循环中的所有运动都必须沿着同一方向进行。换句话说,一个循环中不能同时包括向前的运动和向后的运动。

示例 1:

输入:[2,-1,1,2,2] 输出:true 解释:存在循环,按索引 0 -> 2 -> 3 -> 0 。循环长度为 3 。 示例 2:

输入:[-1,2] 输出:false 解释:按索引 1 -> 1 -> 1 ... 的运动无法构成循环,因为循环的长度为 1 。根据定义,循环的长度必须大于 1 。 示例 3:

输入:[-2,1,-1,-2,-2] 输出:false 解释:按索引 1 -> 2 -> 1 -> ... 的运动无法构成循环,因为按索引 1 -> 2 的运动是向前的运动,而按索引 2 -> 1 的运动是向后的运动。一个循环中的所有运动都必须沿着同一方向进行。

提示:

-1000 ≤ nums[i] ≤ 1000 nums[i] ≠ 0 0 ≤ nums.length ≤ 5000

进阶:

你能写出时间时间复杂度为 O(n) 和额外空间复杂度为 O(1) 的算法吗?

深度搜索每一种可能,从每一个位置都开始深度搜索一遍,只要一个情况有循环,就返回true
如果都不存在的话,就返回false,没有循环

每次深搜都指向下一个位置,用num[idx]保存是否被访问过

class Solution {
    private int len = 0;
    private boolean DFS(int[] nums, int idx, boolean positive)
    {
        if( 0 == nums[idx]) return false;
        if( 1001 == Math.abs(nums[idx])) return true;
        
        int next = (idx + nums[idx]) % len;
        next = (next < 0 ? next + len:next);
        if( positive != (nums[next] > 0) || idx == next) nums[idx] = 0;
        else{
            nums[idx] = (nums[idx] > 0 ? 1001: -1001);
            if(DFS(nums, next, positive)) nums[idx] = 1001;
            else nums[idx] = 0;
        }
        return  0 != nums[idx];
    }
    
    public boolean circularArrayLoop(int[] nums) {
        if(null ==nums || nums.length < 2) return false;
        len = nums.length;
        for(int i=0; i < len; i++)
            if(DFS(nums, i, nums[i] > 0)) return true;
        return false;
    }

}

458. 可怜的小猪

有 1000 只水桶,其中有且只有一桶装的含有毒药,其余装的都是水。它们从外观看起来都一样。如果小猪喝了毒药,它会在 15 分钟内死去。

问题来了,如果需要你在一小时内,弄清楚哪只水桶含有毒药,你最少需要多少只猪?

回答这个问题,并为下列的进阶问题编写一个通用算法。

进阶:

假设有 n 只水桶,猪饮水中毒后会在 m 分钟内死亡,你需要多少猪(x)就能在 p 分钟内找出 “有毒” 水桶?这 n 只水桶里有且仅有一只有毒的桶。

提示:

可以允许小猪同时饮用任意数量的桶中的水,并且该过程不需要时间。 小猪喝完水后,必须有 m 分钟的冷却时间。在这段时间里,只允许观察,而不允许继续饮水。 任何给定的桶都可以无限次采样(无限数量的猪)。

	如果 minutesToTest / minutesToDie = 0,那么每一只猪只有一种状态,即存活。

如果 minutesToTest / minutesToDie = 1,那么每一只猪有两种状态,存活或者死亡。

进一步而言,如果 minutesToTest / minutesToDie = 2,
那么每一只猪有三种状态,存活、在第一次测试后死亡、在第二次测试后死亡。
因此每一只猪的状态数量为 states = minutesToTest / minutesToDie + 1。


 1 只猪可以测试 1 个水桶
 2 只猪可以测试 4 个水桶。
x 只猪可以测试 2^x 个水桶。

在这里插入图片描述

class Solution {
    public int poorPigs(int buckets, int minutesToDie, int minutesToTest) {
 int states = minutesToTest / minutesToDie + 1;
        return (int) Math.ceil(Math.log(buckets) / Math.log(states));
 
    }
}

459. 重复的子字符串

给定一个非空的字符串,判断它是否可以由它的一个子串重复多次构成。给定的字符串只含有小写英文字母,并且长度不超过10000。

示例 1:

输入: "abab"

输出: True

解释: 可由子字符串 "ab" 重复两次构成。 示例 2:

输入: "aba"

输出: False 示例 3:

输入: "abcabcabcabc"

输出: True

解释: 可由子字符串 "abc" 重复四次构成。 (或者子字符串 "abcabc" 重复两次构成。)

递归调用方法,判断是否是可以重复的
如果不是重复的,返回false
如果所有的都是重复的就返回true

每一个位置都开始判断一下
一直把所有的都判断了

class Solution {
     public boolean repeatedSubstringPattern(String s) {
        for(int i=1;i<s.length();i++){
            if(s.length()%i==0){
                if(judge(s.substring(0,i),s)) return true;
            }
        }
        return false;
    }
    public boolean judge(String sub, String S){
        int len = sub.length();
        for(int i=0;i<S.length();i++){
            if(S.charAt(i)!=sub.charAt(i%len)) return false;
        }
        return true;
    }
}