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

312. 戳气球

有 n 个气球,编号为0 到 n-1,每个气球上都标有一个数字,这些数字存在数组 nums 中。

现在要求你戳破所有的气球。每当你戳破一个气球 i 时,你可以获得 nums[left] * nums[i] * nums[right] 个硬币。 这里的 left 和 right 代表和 i 相邻的两个气球的序号。注意当你戳破了气球 i 后,气球 left 和气球 right 就变成了相邻的气球。

求所能获得硬币的最大数量。

说明:

你可以假设 nums[-1] = nums[n] = 1,但注意它们不是真实存在的所以并不能被戳破。 0 ≤ n ≤ 500, 0 ≤ nums[i] ≤ 100 示例:

输入: [3,1,5,8] 输出: 167 解释: nums = [3,1,5,8] --> [3,5,8] --> [3,8] --> [8] --> [] coins = 315 + 358 + 138 + 181 = 167

 ```
 动态规划:
     dp[i][j] =i到j气球得到的最大值
 
 ```
class Solution {
      private int[][] dp;

    public void fill(int[] nums, int from, int to) {
        int max = 0, maxLeft, maxRight, result;
        //假设第i个气球是最后被戳破的
        for (int i = from; i <= to; i++) {
        //每次循环i,就是举例子当前从i分开,左边的和  右边的和,戳破i的和,记录最大值进行比较
            maxLeft = dp[from][i - 1];
            maxRight = dp[i + 1][to];
            result = maxLeft + maxRight + nums[from - 1] * nums[i] * nums[to + 1];
            max = result > max ? result : max;
        }
        dp[from][to] = max;
    }

    public int maxCoins(int[] nums) {
        int length = nums.length;
        dp = new int[length + 2][length + 2];
        for (int i = length + 1; i >= 0; i--) {
            Arrays.fill(dp[i], 0);
        }
        
        int[] expandNums = new int[length + 2];
        expandNums[0] = expandNums[length + 1] = 1;
        System.arraycopy(nums, 0, expandNums, 1, length);
        
        for (int span = 0; span < length; span++) {
            //假设(数组长度 i) 是 i-1
            for (int from = length - span; from > 0; from--) {
                //填充 dp[from][from + span]
                fill(expandNums, from, from + span);
            }
        }
        return dp[1][length];
    }
}

313. 超级丑数

编写一段程序来查找第 n 个超级丑数。

超级丑数是指其所有质因数都是长度为 k 的质数列表 primes 中的正整数。

示例:

输入: n = 12, primes = [2,7,13,19] 输出: 32 解释: 给定长度为 4 的质数列表 primes = [2,7,13,19],前 12 个超级丑数序列为:[1,2,4,7,8,13,14,16,19,26,28,32] 。 说明:

1 是任何给定 primes 的超级丑数。 给定 primes 中的数字以升序排列。 0 < k ≤ 100, 0 < n ≤ 106, 0 < primes[i] < 1000 。 第 n 个超级丑数确保在 32 位有符整数范围内。

一定要结合前面的丑数系列来看,就会很通透
class Solution {
      public int nthSuperUglyNumber(int n, int[] primes) {
            int[] uglyNumbers = new int[n];
            uglyNumbers[0] = 1;
            int primesNumber = primes.length, min = 1, next = 1;
            int[] primeIndexes = new int[primesNumber];
            int[] tempPrimes = new int[primesNumber];

            Arrays.fill(tempPrimes, 1);

            for (int i = 0; i < n; i++) {
                uglyNumbers[i] = min;
                min = Integer.MAX_VALUE;
                for (int j = 0; j < tempPrimes.length; j++) {
                    if (tempPrimes[j] == next) {
                        tempPrimes[j] = primes[j] * uglyNumbers[primeIndexes[j]];
                        primeIndexes[j]++;
                    }
                    min = Math.min(tempPrimes[j], min);
                }
                next = min;
            }

            return uglyNumbers[n - 1];
        }
}

315. 计算右侧小于当前元素的个数

给定一个整数数组 nums,按要求返回一个新数组 counts。数组 counts 有该性质: counts[i] 的值是 nums[i] 右侧小于 nums[i] 的元素的数量。

示例:

输入: [5,2,6,1] 输出: [2,1,1,0] 解释: 5 的右侧有 2 个更小的元素 (2 和 1). 2 的右侧仅有 1 个更小的元素 (1). 6 的右侧有 1 个更小的元素 (1). 1 的右侧有 0 个更小的元素.

class Solution {
     public static  List<Integer> countSmaller(int[] nums) {
        if (nums.length == 0) {
            return new ArrayList<>();
        }
        int min = Integer.MAX_VALUE; // nums数组最小值
        for (int value : nums) {
            if (value < min) {
                min = value;
            }
        }
        for (int i = 0; i < nums.length; i++) {
            nums[i] = nums[i] - min + 1;
        }

        int max = Integer.MIN_VALUE;
        for (int value : nums) {
            if (value > max) {
                max = value;
            }
        }

        int[] BITree = new int[max + 1];
        BITree[0] = 0;
        int[] countArr = new int[nums.length];
        for (int i = nums.length - 1; i >= 0; i--) {
            int count = getSum(nums[i] - 1, BITree);
            countArr[i] = count;
            update(nums[i], BITree);
        }
        List<Integer> result = new ArrayList<>();
        for (int value : countArr) {
            result.add(value);
        }
        return result;
    }
    //获取和
    public static int getSum(int value, int[] BITree) { // 获得a[i]从1,value的和
        int sum = 0;
        while (value > 0) {
            sum += BITree[value];
            value -= (value & -value);
        }
        return sum;
    }
    //单点更新值
    public static void update(int value, int[] BITree) {
        while (value <= BITree.length - 1) {
            BITree[value] += 1;
            value += (value & -value);
        }
    }
//     public List<Integer> countSmaller(int[] nums) {
//         ArrayList<Integer> list = new ArrayList<Integer>();

//         int len = nums.length;
//         Integer[] result = new Integer[len];
//         for(int i = len-1;i>=0;i--) {
//             //将每个数插入到list中//使用二分查找
//             int start = 0; int end = list.size();

//             while(start<end) {
//                 int middle = start+(end-start)/2;
//                 //判断中间的数
//                 if(list.get(middle) < nums[i]) {//严格小于的话,只能在后面部分,并且不包含middle
//                     start = middle+1;
//                 }else {
//                     end = middle;
//                 }
//             }
//             result[i] = start;
//             list.add(start,nums[i]);
//         }
//         return Arrays.asList(result);

//     }
}

316. 去除重复字母

给定一个仅包含小写字母的字符串,去除字符串中重复的字母,使得每个字母只出现一次。需保证返回结果的字典序最小(要求不能打乱其他字符的相对位置)。

示例 1:

输入: "bcabc" 输出: "abc" 示例 2:

输入: "cbacdcbc" 输出: "acdb"

PS: 我把每一个数出现的次数都拿出来,我当前字符比我栈顶的小,并且我栈顶的字符还有多的在后面,我就可以把他替换了,记录一下是否使用

class Solution {
   public String removeDuplicateLetters(String s) {
        int[] charsCount = new int[26];//计算26字母数量
        boolean[] visited = new boolean[26];//标记字母是否已经入栈
        int len = s.length();
        char[] sChars = s.toCharArray();
        for (char c : sChars) {
            charsCount[c - 'a']++;
        }
        Stack<Character> stack = new Stack<>();
        int index = 0;//最终字符的长度
        for (int count : charsCount) {
            if (count > 0) index++;
        }
        char[] res = new char[index];
        for (int i = 0; i < len; i++) {
            char c = s.charAt(i);
            //有小字符的且满足其前面的字符在小字符后还有同样字符的,则出栈
            while (!stack.isEmpty() && c < stack.peek() && charsCount[stack.peek() - 'a'] > 1 && !visited[c - 'a']) {
                Character pop = stack.pop();
                visited[pop - 'a'] = false;
                charsCount[pop - 'a']--;
            }
            if (visited[c - 'a']) {
                charsCount[c - 'a']--;//重复的字符根据游标往后移动,数量减一
                continue;
            }
            stack.push(c);
            visited[c - 'a'] = true;
        }

        while (!stack.isEmpty()) {
            res[--index] = stack.pop();
        }
        return String.valueOf(res);
    }
}

318. 最大单词长度乘积

给定一个字符串数组 words,找到 length(word[i]) * length(word[j]) 的最大值,并且这两个单词不含有公共字母。你可以认为每个单词只包含小写字母。如果不存在这样的两个单词,返回 0。

示例 1:

输入: ["abcw","baz","foo","bar","xtfn","abcdef"] 输出: 16 解释: 这两个单词为 "abcw", "xtfn"。 示例 2:

输入: ["a","ab","abc","d","cd","bcd","abcd"] 输出: 4 解释: 这两个单词为 "ab", "cd"。 示例 3:

输入: ["a","aa","aaa","aaaa"] 输出: 0 解释: 不存在这样的两个单词。

	 全是小写字母, 可以用一个32为整数表示一个word中出现的字母, 
        hash[i]存放第i个单词出现过的字母, a对应32位整数的最后一位,
        b对应整数的倒数第二位, 依次类推. 时间复杂度O(N^2)
        判断两两单词按位与的结果, 如果结果为0且长度积大于最大积则更新
class Solution {
   public int maxProduct(String[] words) {
        int n = words.length;
        int[] wordsBit = new int[n];
        for (int i = 0; i < n; i++) {
            wordsBit[i] = convertWordToBit(words[i]);
        }
        int max = 0;
        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j < n; j++) {
                if ((wordsBit[i] & wordsBit[j]) == 0) {
                    int newLength = words[i].length() * words[j].length();
                    max = max < newLength ? newLength : max;
                }
            }
        }
        return max;
    }

    private int convertWordToBit(String word) {
        int wordBit = 0;
        int n = word.length();
        for (int i = 0; i < n; i++) {
            wordBit = wordBit | (1 << (word.charAt(i) - 'a'));
        }
        return wordBit;
    }
}

319. 灯泡开关

初始时有 n 个灯泡关闭。 第 1 轮,你打开所有的灯泡。 第 2 轮,每两个灯泡你关闭一次。 第 3 轮,每三个灯泡切换一次开关(如果关闭则开启,如果开启则关闭)。第 i 轮,每 i 个灯泡切换一次开关。 对于第 n 轮,你只切换最后一个灯泡的开关。 找出 n 轮后有多少个亮着的灯泡。

示例:

输入: 3 输出: 1 解释: 初始时, 灯泡状态 [关闭, 关闭, 关闭]. 第一轮后, 灯泡状态 [开启, 开启, 开启]. 第二轮后, 灯泡状态 [开启, 关闭, 开启]. 第三轮后, 灯泡状态 [开启, 关闭, 关闭].

你应该返回 1,因为只有一个灯泡还亮着。

	初始有n个灯泡关闭
  第i轮的操作是每i个灯泡切换一次开关(开->闭,闭->开),即切换i的倍数位置的开关。
 求n轮后亮着的灯泡?
 * (1)第i轮时,被切换的灯泡位置是i的倍数。
 * (2)由(1)得出,对于第p个灯泡来说,只有其第“因子”轮才会切换,若其有q个因子,则最终被切换q次。
         因为初始状态是关闭状态,那么因子数是奇数的灯泡最终是亮着的。
 * (3)只有平方数的因子个数不是成对出现,举例:4=1 * 4,2 * 2,其因子是1,2,4。
 * (4)那么题目最终转化为1~n里平方数的个数,进而转化为对n开平方根,向下取整即可。
class Solution {
    public int bulbSwitch(int n) {
        return (int) Math.floor(Math.sqrt(n));
    }
}