从今天开始用Java写了

Easy

[338] 比特位计数

dynamic-programming | bit-manipulation

2022/04/07

class Solution {
/**
* 15/15 cases passed (1 ms)
* Your runtime beats 99.97 % of java submissions
* Your memory usage beats 24.73 % of java submissions (45.5 MB)
*
* @param n int
* @return
*/
public int[] countBits(int n) {
int k = 1;
int[] ans = new int[n + 1];
for (int i = 1; i <= n; i++) {
if (i == k) {
ans[i] = 1;
k *= 2;
} else {
ans[i] = 1 + ans[i - k / 2];
}
}
return ans;
}
}

Medium

[279] 完全平方数

math | dynamic-programming | breadth-first-search

2022/04/05

class Solution {
/**
* 588/588 cases passed (42 ms)
* Your runtime beats 47.8 % of java submissions
* Your memory usage beats 55.32 % of java submissions (40.5 MB)
*
* @param n int
* @return 和为 n 的完全平方数的最少数量 int
*/
public int numSquares(int n) {
int[] dp = new int[n];
int k = 1;
for (int i = 0; i < n; i++) {
if (i + 1 == k * k) {
dp[i] = 1;
k++;
} else {
dp[i] = Integer.MAX_VALUE;
for (int j = 1; j < k; j++) {
dp[i] = Math.min(dp[i], 1 + dp[i - j * j]);
}
}
}
return dp[n - 1];
}
}

[287] 寻找重复数

array | two-pointers | binary-search

2022/04/05

class Solution {
/**
* 因为特殊的数量,所以可以当作有环链表考虑,化为求链表的环的入口
* 举个例子:nums =
* [2,5,9,6,9,3,8,9,7,1],构造成链表就是:2->[9]->1->5->3->6->8->7->[9],也就是在[9]处循环。
* 快慢指针问题,会在环内的[9]->1->5->3->6->8->7->[9]任何一个节点追上
* 不一定是在[9]处相碰,事实上会在7处碰上。
* 证明为什么fast要归零:
* 快指针每次走2步,慢指针每次走1步。
* 设相遇时快指针走t2步,慢指针走t1步,环长为c。
* 则相遇时, 快指针比慢指针多走一个环的长度,即 t2 = t1 + c。
* 又t2 = 2t1 (快指针走的步数是慢指针的两倍) 则 2t1 = t1 + c, t1=c,即慢指针走了c步。
* 设环起点在第k步,显然慢指针再走k步就会到达环的终点(因为路径总长为c+k),也是环的起点。
* 如果设置一个i指针从起点开始走,则慢指针和i指针会在环起点相碰。
*
* 58/58 cases passed (4 ms)
* Your runtime beats 94.21 % of java submissions
* Your memory usage beats 57.73 % of java submissions (58.4 MB)
*
* @param nums int[]
* @return
*/
public int findDuplicate(int[] nums) {
int fast = nums[nums[0]], slow = nums[0];
while (fast != slow) {
fast = nums[nums[fast]];
slow = nums[slow];
}
fast = 0;
while (nums[slow] != nums[fast]) {
fast = nums[fast];
slow = nums[slow];
}
return nums[slow];
}
}

[309] 最佳买卖股票时机含冷冻期

dynamic-programming

2022/04/06

class Solution {

/**
* 买在冷却期后,所以dp(买)要看dp(冷却期)
* 卖在买后,所以dp(卖)要看dp(买)
* 冷却期在卖后,所以dp(冷却期)要看dp(卖)
*
* 210/210 cases passed (0 ms)
* Your runtime beats 100 % of java submissions
* Your memory usage beats 12.76 % of java submissions (39.8 MB)
*
* @param prices int[]
* @return
*/
public int maxProfit(int[] prices) {
int n = prices.length;
int[] buy = new int[n];
int[] sell = new int[n];
int[] frozen = new int[n];
buy[0] -= prices[0];
for (int i = 1; i < n; i++) {
buy[i] = Math.max(frozen[i - 1] - prices[i], buy[i - 1]);
sell[i] = Math.max(buy[i - 1] + prices[i], sell[i - 1]);
frozen[i] = Math.max(sell[i - 1], frozen[i - 1]);
}
return Math.max(buy[n - 1], Math.max(sell[n - 1], frozen[n - 1]));
}
}

[337] 打家劫舍 III

tree | depth-first-search

2022/04/06

// @formatter:off
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
// @formatter:on
class Solution {
private Map<TreeNode, Integer> chosenDp = new HashMap<>();
private Map<TreeNode, Integer> noChosenDp = new HashMap<>();

/**
* 还能用集合dp我是没想到的
*
* 124/124 cases passed (3 ms)
* Your runtime beats 19.55 % of java submissions
* Your memory usage beats 21.06 % of java submissions (41.1 MB)
*
* @param root TreeNode
* @return
*/
public int rob(TreeNode root) {
dfs(root);
return Math.max(chosenDp.getOrDefault(root, 0), noChosenDp.getOrDefault(root, 0));
}

private void dfs(TreeNode node) {
if (node == null) {
return;
} else {
dfs(node.left);
dfs(node.right);
chosenDp.put(node,
node.val + noChosenDp.getOrDefault(node.left, 0) + noChosenDp.getOrDefault(node.right, 0));
noChosenDp.put(node, Math.max(chosenDp.getOrDefault(node.left, 0), noChosenDp.getOrDefault(node.left, 0))
+ Math.max(chosenDp.getOrDefault(node.right, 0), noChosenDp.getOrDefault(node.right, 0)));
}
}
}

[347] 前 K 个高频元素

hash-table | heap

2022/04/08

class Solution {
/**
* 官方题解,之前不知道堆怎么做,原来还有优先队列这么一种数据结构可以使用
* 真是小刀剌屁股——开眼了
*
* 21/21 cases passed (12 ms)
* Your runtime beats 90.06 % of java submissions
* Your memory usage beats 64.44 % of java submissions (43.6 MB)
*
* @param nums int[]
* @param k int
* @return
*/
public int[] topKFrequent(int[] nums, int k) {
Map<Integer, Integer> occurrences = new HashMap<Integer, Integer>();
for (int num : nums) {
occurrences.put(num, occurrences.getOrDefault(num, 0) + 1);
}
PriorityQueue<int[]> queue = new PriorityQueue<int[]>(new Comparator<int[]>() {
public int compare(int[] m, int[] n) {
return m[1] - n[1];
}
});
for (Map.Entry<Integer, Integer> entry : occurrences.entrySet()) {
int num = entry.getKey(), count = entry.getValue();
if (queue.size() == k) {
if (queue.peek()[1] < count) {
queue.poll();
queue.offer(new int[] { num, count });
}
} else {
queue.offer(new int[] { num, count });
}
}
int[] ret = new int[k];
for (int i = 0; i < k; ++i) {
ret[i] = queue.poll()[0];
}
return ret;
}
}

[394] 字符串解码

stack | depth-first-search

2022/04/08

class Solution {
/**
* 除了烦以外没有任何技术水平
*
* 34/34 cases passed (1 ms)
* Your runtime beats 77.92 % of java submissions
* Your memory usage beats 43.71 % of java submissions (39.4 MB)
*
* @param s String
* @return
*/
public String decodeString(String s) {
StringBuffer ans = new StringBuffer(s);
Stack<int[]> leftParenthesis = new Stack<>();
for (int i = 0; i < ans.length(); i++) {
if (ans.charAt(i) == '[') {
int num = 0, k = 1, j = i - 1;
for (; j >= 0; j--) {
if (ans.charAt(j) < '0' || ans.charAt(j) > '9') {
break;
} else {
num += k * (ans.charAt(j) - '0');
k *= 10;
}
}
int[] tmp = { j + 1, i, num };
leftParenthesis.add(tmp);
} else if (ans.charAt(i) == ']') {
int[] last = leftParenthesis.pop();
StringBuffer decodedString = new StringBuffer();
for (int j = 0; j < last[2]; j++) {
decodedString.append(ans.substring(last[1] + 1, i));
}
ans.replace(last[0], i + 1, decodedString.toString());
i = last[0] + decodedString.length() - 1;
}
}
return ans.toString();
}
}

Hard

[301] 删除无效的括号

depth-first-search | breadth-first-search

2022/04/06

class Solution {
/**
* 官方题解方法
* 为何用BFS?
* 利用BFS理解起来要远远比DFS要简单的多,因为这道题说的是删除最少的括号!!
* 如果我们每次只删除一个括号,然后观察被删除一个括号后是否合法,如果已经合法了,我们就不用继续删除了。
* 因此我们并不需要将遍历进行到底,而是层层深入,一旦达到需求,就不再深入了。
*
* 本来以为必是用bp的,妹想到啊
*
* 127/127 cases passed (34 ms)
* Your runtime beats 48.24 % of java submissions
* Your memory usage beats 7.78 % of java submissions (42.3 MB)
*
* @param s String
* @return
*/
public List<String> removeInvalidParentheses(String s) {
List<String> ans = new ArrayList<>();
Set<String> bfs = new HashSet<>();
bfs.add(s);
while (true) {
for (String str : bfs) {
if (isValid(str)) {
ans.add(str);
}
}
if (!ans.isEmpty()) {
return ans;
}
Set<String> nextBfs = new HashSet<>();
for (String str : bfs) {
for (int i = 0; i < str.length(); i++) {
if (i > 0 && str.charAt(i) == str.charAt(i - 1)) {
continue;
} else if (str.charAt(i) == '(' || str.charAt(i) == ')') {
nextBfs.add(str.substring(0, i) + str.substring(i + 1));
}
}
}
bfs = nextBfs;
}
}

private Boolean isValid(String str) {
int count = 0;
for (int i = 0; i < str.length(); i++) {
if (str.charAt(i) == '(') {
count++;
} else if (str.charAt(i) == ')') {
count--;
if (count < 0) {
return false;
}
}
}
return count == 0;
}
}

[312] 戳气球

divide-and-conquer | dynamic-programming

2022/04/05

class Solution {
/**
* dp[i][j]表示填满开区间(i,j)能得到的最多硬币数
*
* 戳掉第k个,再计算dp[i][k]和dp[k][j]
*
* 73/73 cases passed (34 ms)
* Your runtime beats 90.08 % of java submissions
* Your memory usage beats 40.03 % of java submissions (41.1 MB)
*
* @param nums int[]
* @return
*/
public int maxCoins(int[] nums) {
int n = nums.length;
int[][] dp = new int[n + 2][n + 2];
int[] val = new int[n + 2];
val[0] = val[n + 1] = 1;
for (int i = 1; i <= n; i++) {
val[i] = nums[i - 1];
}
for (int i = n - 1; i >= 0; i--) {
for (int j = i + 2; j <= n + 1; j++) {
for (int k = i + 1; k < j; k++) {
int sum = val[i] * val[k] * val[j];
sum += dp[i][k] + dp[k][j];
dp[i][j] = Math.max(dp[i][j], sum);
}
}
}
return dp[0][n + 1];
}
}