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));
}
}
















