LeetCode 401-500

24576 字
123 分钟
LeetCode 401-500

LeetCode Problems 401-500.

401. Binary Watch#

https://leetcode.com/problems/binary-watch/


直接调用Integer.bitCount()函数,如果时和分中的1相加等于turnedOn,就添加到结果中。

class Solution {
public List<String> readBinaryWatch(int turnedOn) {
List<String> result = new ArrayList<>();
for (int hour = 0; hour < 12; hour++) {
for (int minute = 0; minute < 60; minute++) {
if (Integer.bitCount(hour) + Integer.bitCount(minute) == turnedOn) {
result.add(hour + (minute < 10 ? ":0" : ":") + minute);
}
}
}
return result;
}
}

402. Remove K Digits#

https://leetcode.com/problems/remove-k-digits/


遍历字符串,找到递减序列,定位到第一个需要被替换的“大”数字,从之后找到更小的数字覆盖它。如果循环结束后,k的次数仍有剩余,根据endk的大小关系,判断返回值。

class Solution {
public String removeKdigits(String num, int k) {
int end = 0;
char[] array = num.toCharArray();
for (int i = 0; i < num.length(); i++) {
// encounter a decreasing sequence, locate the digit to be overwritten
while (end > 0 && k > 0 && array[end - 1] > array[i]) {
k--;
end--;
}
// skip leading '0' and overwrite one digit with a digit after
if (end > 0 || array[i] != '0') {
array[end] = array[i];
end++;
}
}
// if there are still k left, return 0 or truncate the last k digits
return end <= k ? "0" : String.valueOf(array, 0, end - k);
}
}

403. Frog Jump#

https://leetcode.com/problems/frog-jump/


DFS思想,每一跳都尝试三种长度可能。同时可以用一个map存储每个下标与对应的k值,如果我们此前已经用k值到达过这个下标,就记录下来,避免之后重复DFS。

class Solution {
// <index, set of all possible k>
Map<Integer, Set<Integer>> checked = new HashMap<>();
public boolean canCross(int[] stones) {
for (int i = 0; i < stones.length; i++) {
checked.put(stones[i], new HashSet<>());
}
return helper(0, 1, stones[stones.length - 1]);
}
private boolean helper(int current, int k, int last) {
if (current == last) {
return true;
}
// skip if we have checked this k at the current stone
if (k <= 0 || checked.get(current).contains(k)) {
return false;
}
int next = current + k;
if (checked.containsKey(next)) {
if (helper(next, k - 1, last) || helper(next, k, last) || helper(next, k + 1, last)) {
return true;
}
}
checked.get(current).add(k);
return false;
}
}

404. Sum of Left Leaves#

https://leetcode.com/problems/sum-of-left-leaves/


从根节点开始:

  1. 如果root.left不空,则检查左子树
    • 如果root.left.left已经是叶节点,直接加上它的值
    • 否则,递归加上左子树中所有左叶节点的和
  2. 如果root.right不空,检查右子树中所有左叶节点的和
/**
* 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;
* }
* }
*/
class Solution {
public int sumOfLeftLeaves(TreeNode root) {
int result = 0;
if (root == null) {
return result;
}
if (root.left != null) {
if (root.left.left == null && root.left.right == null) {
result += root.left.val;
} else {
result += sumOfLeftLeaves(root.left);
}
}
if (root.right != null) {
result += sumOfLeftLeaves(root.right);
}
return result;
}
}

405. Convert a Number to Hexadecimal#

https://leetcode.com/problems/convert-a-number-to-hexadecimal/


使用循环,每次提取num最后4位转成16进制,再将最后的结果倒转。

class Solution {
public String toHex(int num) {
if (num == 0) {
return "0";
}
char[] map = new char[] {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
StringBuilder sb = new StringBuilder();
while (num != 0) {
// transform last 4 bits to hex
sb.append(map[num & 15]);
// get rid of the last 4 bits
num >>>= 4;
}
return sb.reverse().toString();
}
}

406. Queue Reconstruction by Height#

https://leetcode.com/problems/queue-reconstruction-by-height/


本质而言,队列的恢复是一个排序并且重新插入元素的过程。

  1. 对输入数组进行排序。排序规则是:默认按照h(身高)进行排序,身高越高排在越前面;身高一样时,按照k(面前身高大于等于h的人数)进行排序,人数越少越靠前
  2. 把排序好的队列插入到List中,注意插入时需要参考k值,k值是多少就插入到哪个位置。因为上一步中已经排序,而此时又是按照排序后的顺序进行遍历,因此k值相同时,身高矮的人比身高高的人后插入,起到了调整顺序的作用

最后,把List转为array即可。

class Solution {
public int[][] reconstructQueue(int[][] people) {
Arrays.sort(people, (a, b) -> (a[0] == b[0] ? a[1] - b[1] : b[0] - a[0]));
List<int[]> result = new LinkedList<>();
for (int[] p : people) {
result.add(p[1], p);
}
return result.toArray(new int[result.size()][]);
}
}

407. Trapping Rain Water II#

https://leetcode.com/problems/trapping-rain-water-ii/


核心思路是:把每个block定义成Comparable的object,这样能够利用Priority Queue根据每个Block的高度进行排序。之后,从map的四条边缘开始,把四条边上的所有Block加入到Priority Queue中,开始进行BFS遍历。

BFS遍历的流程是:从边缘Block开始,向四个方向的邻近Block遍历,筛除掉超过map边界和已访问Block的情况。之后按照高度进行判断:如果邻近的Block比现有的这个更高,就把它加入到Priority Queue中,用于后续进一步的判断,并且中止BFS;而如果它更矮,说明可以trap一些水,水的体积就是两个Block的高度差,并且继续进行BFS。

因为使用了Priority Queue,因此能够确保遍历Block的时候一定是按照从低到高的顺序开始,因此能保证这种方式trap的水量是最大的。

class Solution {
private int water = 0;
private boolean[][] visited;
private int visitedCnt = 0;
private static class Block implements Comparable<Block> {
int row;
int col;
int value;
Block(int row, int col, int value) {
this.row = row;
this.col = col;
this.value = value;
}
@Override
public int compareTo(Block block) {
return value - block.value;
}
}
public int trapRainWater(int[][] heightMap) {
if (heightMap == null || heightMap.length < 3 || heightMap[0].length < 3) {
return 0;
}
int rows = heightMap.length;
int cols = heightMap[0].length;
PriorityQueue<Block> pq = new PriorityQueue<Block>();
visited = new boolean[rows][cols];
// add row edge blocks to priority queue
for (int i = 0; i < cols; i++) {
Block block1 = new Block(0, i, heightMap[0][i]);
Block block2 = new Block(rows - 1, i, heightMap[rows - 1][i]);
pq.offer(block1);
pq.offer(block2);
visited[0][i] = true;
visited[rows - 1][i] = true;
visitedCnt += 2;
}
// add column edge blocks to priority queue
for (int i = 1; i < rows - 1; i++) {
Block block1 = new Block(i, 0, heightMap[i][0]);
Block block2 = new Block(i, cols - 1, heightMap[i][cols - 1]);
pq.offer(block1);
pq.offer(block2);
visited[i][0] = true;
visited[i][cols - 1] = true;
visitedCnt += 2;
}
// BFS
while (visitedCnt < rows * cols) {
Block block = pq.poll();
fill(block.row - 1, block.col, block.value, pq, heightMap);
fill(block.row, block.col - 1, block.value, pq, heightMap);
fill(block.row + 1, block.col, block.value, pq, heightMap);
fill(block.row, block.col + 1, block.value, pq, heightMap);
}
return water;
}
public void fill(int row, int col, int min, PriorityQueue<Block> pq, int[][] heightMap) {
if (row < 0 || col < 0 || row >= heightMap.length || col >= heightMap[0].length || visited[row][col]) {
// out of bound, or visited block
return;
} else if (heightMap[row][col] >= min) {
// visit a higher block, add to queue and mark it as visited
Block newBlock = new Block(row, col, heightMap[row][col]);
pq.offer(newBlock);
visited[row][col] = true;
visitedCnt++;
} else {
// visit a lower block, add water to be trapped
water += min - heightMap[row][col];
visited[row][col] = true;
visitedCnt++;
fill(row - 1, col, min, pq, heightMap);
fill(row, col - 1, min, pq, heightMap);
fill(row + 1, col, min, pq, heightMap);
fill(row, col + 1, min, pq, heightMap);
}
}
}

408. Valid Word Abbreviation#

https://leetcode.com/problems/valid-word-abbreviation/


使用两个指针分别遍历wordabbr,如果abbr遇到了数字,判断这个数字number是多少,直到数字结束;否则,让word的指针跳过number的下标,看此时wordabbr下标对应的字母仍然相等。重复这个循环,最后判断两个指针是否能分别完成遍历。

class Solution {
public boolean validWordAbbreviation(String word, String abbr) {
int number = 0;
int i = 0, j = 0;
while (i < word.length() && j < abbr.length()) {
if (abbr.charAt(j) >= '0' && abbr.charAt(j) <= '9') {
number = number * 10 + abbr.charAt(j) - '0';
if (number == 0) {
return false;
}
j++;
} else {
i += number;
if (i >= word.length() || word.charAt(i) != abbr.charAt(j)) {
return false;
}
number = 0;
i++;
j++;
}
}
// abbr ends with number
i += number;
return i == word.length() && j == abbr.length();
}
}

409. Longest Palindrome#

https://leetcode.com/problems/longest-palindrome/


统计每个字符出现的次数,用一个变量odd记录出现次数为奇数的字符数量。如果odd最后小于2,说明可以直接用s构造最长的回文串;反之,需要剔除掉odd - 1个这种奇数字符(只能选一个字符全部加入,其他的奇数字符每个要减掉一个)。

class Solution {
public int longestPalindrome(String s) {
int[] freq = new int[128];
for (int i = 0; i < s.length(); i++) {
freq[s.charAt(i)]++;
}
int odd = 0;
for (int i = 0; i < freq.length; i++) {
if (freq[i] % 2 != 0) {
odd++;
}
}
return odd < 2 ? s.length() : s.length() - odd + 1;
}
}

410. Split Array Largest Sum#

https://leetcode.com/problems/split-array-largest-sum/


如果mnums的个数相等,那么每个数组都是一个子数组,所以返回nums中最大的数字即可;如果m为1,那么整个nums数组就是一个子数组,返回nums所有数字之和,所以对于其他有效的m值,返回的值必定在上面两个值之间,可以用二分搜索法来做。

判断和是否满足条件的办法:遍历找到数组的最大值作为初始minVal,数组的和为初始maxVal,选择mid作为二者的平均值。从数组头开始遍历,计算累加和大于等于mid的子数组总量。如果子数组的数量大于m,说明子数组太多了,也就是说区间的和定义的太小了,应该增大minVal的值,因此把minVal设置为mid + 1;相反,则要降低maxVal的值。

class Solution {
public int splitArray(int[] nums, int m) {
int minVal = Integer.MIN_VALUE, maxVal = 0;
for (int num : nums) {
minVal = Math.max(minVal, num);
maxVal += num;
}
while (minVal < maxVal) {
int mid = minVal + (maxVal - minVal) / 2;
if (canSplit(mid, nums, m)) {
maxVal = mid;
} else {
minVal = mid + 1;
}
}
return minVal;
}
private boolean canSplit(int upperBoundSubarraySum, int[] nums, int m) {
int curSubarraySum = 0, subarrayCount = 1;
for (int num : nums) {
curSubarraySum += num;
if (curSubarraySum > upperBoundSubarraySum) {
subarrayCount++;
curSubarraySum = num;
if (subarrayCount > m) {
return false;
}
}
}
return true;
}
}

411. Minimum Unique Word Abbreviation#

https://leetcode.com/problems/minimum-unique-word-abbreviation/


尝试利用位掩码来快速表示不同字符串之间的差别。具体而言,用一个整数diff表示字符串wordtarget之间的差别,假设要比较它们第i位的字符是否相等,如果不相等,diff就加上1 << i。解题流程如下:

  1. 对于给定的target,在dic中寻找和target长度相同的word,记录下表示wordtarget之间的差异的diff,用一个Set存起来
  2. 如果经过第一步后Set为空,说明不存在缩写冲突的可能,当前字符串直接使用字符串长度作为最短缩写
  3. 寻找最大的冲突表示整数bits。具体而言,需要穷举0 ~ 1 << targetLen之间的数与diff进行比较,跳过完全与diff完全不相等的情况,并在剩下的情况中用位运算寻找bits
  4. 根据最大冲突位数生成最终的缩略字符串。将bits1 << i逐位进行对比,如果这当前位相等,说明此时出现冲突,不能用数字缩略,必须将之前的缩略长度添加到缩略字符串后清空,再加上当前位置的字符
class Solution {
public String minAbbreviation(String target, String[] dic) {
Set<Integer> diffs = new HashSet<>();
int targetLen = target.length();
for (String word : dic) {
if (word.length() == targetLen) {
// mark each bit's difference between target and word
int diff = 0;
for (int i = 0; i < targetLen; i++) {
if (target.charAt(i) != word.charAt(i)) {
diff += 1 << i;
}
}
diffs.add(diff);
}
}
// all words have different length, then return the shortest abbr
if (diffs.isEmpty()) {
return Integer.toString(targetLen);
}
int bits = Integer.MIN_VALUE;
int max = Integer.MIN_VALUE;
for (int i = 0; i < (1 << targetLen); i++) {
boolean flag = true;
// skip cases that are totally different from dic
for (int diff : diffs) {
if ((i & diff) == 0) {
flag = false;
break;
}
}
// get maximum conflicting bits
if (flag) {
int sum = 0;
for (int j = 0; j < targetLen - 1; j++) {
if (((i >> j) & 3) == 0) {
sum++;
}
}
if (sum > max) {
max = sum;
bits = i;
}
}
}
StringBuilder result = new StringBuilder();
int count = 0;
for (int i = 0; i < targetLen; i++) {
// current pos cannot be replaced with an abbr digit, must use alphabet instead
if ((bits & (1 << i)) != 0) {
if (count != 0) {
result.append(count);
count = 0;
}
result.append(target.charAt(i));
} else {
count++;
}
}
if (count != 0) {
result.append(count);
}
return result.toString();
}
}

412. Fizz Buzz#

https://leetcode.com/problems/fizz-buzz/


直接使用常规循环判断法解题即可。

class Solution {
public List<String> fizzBuzz(int n) {
List<String> result = new ArrayList<>();
for (int i = 1; i <= n; i++) {
if (i % 3 == 0 && i % 5 == 0) {
result.add("FizzBuzz");
} else if (i % 3 == 0) {
result.add("Fizz");
} else if (i % 5 == 0) {
result.add("Buzz");
} else {
result.add(String.valueOf(i));
}
}
return result;
}
}

如果想让代码更加优雅,并且在未来具有良好的拓展性,可以设计一个TreeMap,存放所有的编码规则,在循环遍历时直接去Map中找当前数字所有匹配的规则。

class Solution {
public List<String> fizzBuzz(int n) {
// define the order of how you want to concat the words here using lambda comparator
TreeMap<Integer, String> map = new TreeMap<>((a, b) -> a - b);
map.put(3, "Fizz");
map.put(5, "Buzz");
// map.put(7, "Yuzz");
// map.put(9, "Mozz");
// add more encoding options here ...
List<String> result = new LinkedList();
for (int i = 1; i <= n; i++) {
StringBuilder encoded = new StringBuilder();
for (int num : map.keySet()) {
if (i % num == 0) {
encoded.append(map.get(num));
}
}
result.add((encoded.length() == 0) ? String.valueOf(i) : encoded.toString());
}
return result;
}
}

413. Arithmetic Slices#

https://leetcode.com/problems/arithmetic-slices/


循环遍历数组,只需要跟踪当前下标数与之前的两个数,判断这三个数能不能成为一组新的slice。如果可以,当前newSlice数量加1,并且注意实时更新结果数量时,要添加的是目前的newSlice数量而不是1(因为当前newSlice每加一,就代表前面已有的slice都可以再拓展一位当前数,组成一个新的slice)。而一旦三个数中断组成slice,newSlice全要被清零。

class Solution {
public int numberOfArithmeticSlices(int[] A) {
int newSlice = 0, result = 0;
for (int i = 2; i < A.length; i++) {
if (A[i-1] - A[i-2] == A[i] - A[i-1]) {
newSlice++;
result += newSlice;
} else {
newSlice = 0;
}
}
return result;
}
}

414. Third Maximum Number#

https://leetcode.com/problems/third-maximum-number/


使用三变量法进行统计,注意变量初始化时声明为null更加严谨。

class Solution {
public int thirdMax(int[] nums) {
Integer max1 = null;
Integer max2 = null;
Integer max3 = null;
for (Integer n : nums) {
if (n.equals(max1) || n.equals(max2) || n.equals(max3)) {
continue;
}
if (max1 == null || n > max1) {
max3 = max2;
max2 = max1;
max1 = n;
} else if (max2 == null || n > max2) {
max3 = max2;
max2 = n;
} else if (max3 == null || n > max3) {
max3 = n;
}
}
return max3 == null ? max1 : max3;
}
}

如果要拓展成更加一般性的第k大数字,可以使用优先队列+集合。

class Solution {
public int thirdMax(int[] nums) {
PriorityQueue<Integer> pq = new PriorityQueue<>();
Set<Integer> set = new HashSet<>();
for (int n : nums) {
if (set.add(n)) {
pq.offer(n);
if (pq.size() > 3) {
pq.poll();
}
}
}
if (pq.size() == 2) {
pq.poll();
}
return pq.peek();
}
}

415. Add Strings#

https://leetcode.com/problems/add-strings/


仍然是模拟加法,从字符串尾部开始,取数 -> 做加法 -> 得到进位,循环直到两个数都完整被遍历过一次。

class Solution {
public String addStrings(String num1, String num2) {
StringBuilder result = new StringBuilder();
int carry = 0;
int len1 = num1.length() - 1;
int len2 = num2.length() - 1;
while (len1 >= 0 || len2 >= 0) {
int x = len1 >= 0 ? num1.charAt(len1) - '0' : 0;
int y = len2 >= 0 ? num2.charAt(len2) - '0' : 0;
int sum = (x + y + carry) % 10;
carry = (x + y + carry) / 10;
result.append(sum);
len1--;
len2--;
}
if (carry != 0) {
result.append(carry);
}
return result.reverse().toString();
}
}

416. Partition Equal Subset Sum#

https://leetcode.com/problems/partition-equal-subset-sum/


使用DFS,把问题转化成选若干个数,使得总和恰好为sum的一半。注意考虑两种情况:

  • 选择当前数字,并修改下一次递归的target
  • 不选择当前数字,而是找到下一个不同的数字并选择它进行递归
class Solution {
public boolean canPartition(int[] nums) {
int sum = 0;
for (int val: nums) sum += val;
if (sum % 2 == 1) {
return false;
}
sum /= 2;
Boolean[] dp = new Boolean[sum + 1];
dp[0] = true;
boolean result = canPartition(sum, 0, nums, dp);
return result;
}
private boolean canPartition(int sum, int pos, int[] nums, Boolean[] dp) {
if (sum == 0) {
return true;
}
if (sum < 0 || pos == nums.length) {
return false;
}
if (dp[sum] != null) {
return dp[sum];
}
dp[sum] = canPartition(sum - nums[pos], pos + 1, nums, dp) || canPartition(sum, pos + 1, nums, dp);
return dp[sum];
}
}

417. Pacific Atlantic Water Flow#

https://leetcode.com/problems/pacific-atlantic-water-flow/


使用DFS逆向推导,因为边缘的格子一定能够进入海洋。逆向推导的过程中,用两个二维数组分别表示当前的坐标是否能进入太平洋/大西洋。注意,因为两个海洋对应四条边,因此需要以四条边的每个点做为起点进行DFS。

class Solution {
public List<List<Integer>> pacificAtlantic(int[][] matrix) {
List<List<Integer>> result = new ArrayList<>();
if (matrix.length == 0 || matrix[0].length == 0) {
return result;
}
int m = matrix.length;
int n = matrix[0].length;
boolean[][] pacific = new boolean[m][n];
boolean[][] atlantic = new boolean[m][n];
// top and bottom
for (int col = 0; col < n; col++) {
dfs(matrix, 0, col, matrix[0][col], pacific);
dfs(matrix, m-1, col, matrix[m-1][col], atlantic);
}
// left and right
for (int row = 0; row < m; row++) {
dfs(matrix, row, 0, matrix[row][0], pacific);
dfs(matrix, row, n-1, matrix[row][n-1], atlantic);
}
// find all grids that meet requirements
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (pacific[i][j] && atlantic[i][j]) {
List<Integer> temp = new ArrayList();
temp.add(i);
temp.add(j);
result.add(temp);
}
}
}
return result;
}
private void dfs(int[][] matrix, int r, int c, int preHeight, boolean[][] ocean) {
// skip out-of-bound & higher flow & visited cases
if (r < 0 || c < 0 || r > matrix.length-1 || c > matrix[0].length-1 || matrix[r][c] < preHeight || ocean[r][c]) {
return;
}
ocean[r][c] = true;
dfs(matrix, r - 1, c, matrix[r][c], ocean); // up
dfs(matrix, r + 1, c, matrix[r][c], ocean); // down
dfs(matrix, r, c - 1, matrix[r][c], ocean); // left
dfs(matrix, r, c + 1, matrix[r][c], ocean); // right
}
}

418. Sentence Screen Fitting#

https://leetcode.com/problems/sentence-screen-fitting/


使用数组times记录每一行完成遍历的次数;使用数组nextIndex记录下一行的起始单词对应下标。

中间的循环,本质是遍历单词填满每一行长度的过程,如果填满了就保存下一行的起始下标与这一行的次数,之后跳下一行;如果这一行完成了遍历,还要更新遍历次数。最后,根据rows的长度,顺序累加遍历次数即可,检查完一行需要根据下一行起始下标进行跳转。

class Solution {
public int wordsTyping(String[] sentence, int rows, int cols) {
// a new line which is starting with certain index in sentence, what is the starting index of next line
int[] nextIndex = new int[sentence.length];
// how many times the pointer in current line passes over the last index
int[] times = new int[sentence.length];
for (int i = 0; i < sentence.length; i++) {
int curLen = 0;
int cur = i;
int time = 0;
while (curLen + sentence[cur].length() <= cols) {
// word + space
curLen += sentence[cur].length() + 1;
cur++;
// the whole sentence is added for a time
if (cur == sentence.length) {
cur = 0;
time++;
}
}
// stop at the first word of next line and times of the previous line
nextIndex[i] = cur;
times[i] = time;
}
int result = 0;
int cur = 0; // start from the first word in the sentence
for (int i = 0; i < rows; i++) {
result += times[cur];
cur = nextIndex[cur]; // update start of next row
}
return result;
}
}

419. Battleships in a Board#

https://leetcode.com/problems/battleships-in-a-board/


因为战舰的构造一定是1*N或者N*1,因此不需要搜索,直接两重循环遍历,检查当前方格是否处在一行或一列’X’中,具体而言只需要循环和左、上邻近方格比较即可。

class Solution {
public int countBattleships(char[][] board) {
if (board.length == 0 || board[0].length == 0) {
return 0;
}
int result = 0;
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board[0].length; j++) {
if (board[i][j] == '.') {
continue;
}
// 1 x N or N x 1, thus we can always check left or up grid to determine the same battleship
if ((i > 0 && board[i-1][j] == 'X') || (j > 0 && board[i][j-1] == 'X')) {
continue;
}
result++;
}
}
return result;
}
}

420. Strong Password Checker#

https://leetcode.com/problems/strong-password-checker/


记录下缺失的字符类型数量(小写、大写、数字),再用一个数组记录字符串截止到每一位时的重复字符数量。

  • 如果字符串太短,检查是否需要补缺失的字符,再看剩下的长度是否达到至少6位
  • 删减重复子字符串,根据每位重复字符数量,把大于2的情况删减修改成2
  • 如果字符串很长,就先做删减重复子字符串,看删完之后剩下的长度是否还超过20位,并视需要补缺失字符
class Solution {
public int strongPasswordChecker(String s) {
int result = 0;
// number of missing lowercase letters, uppercase letters and digits
int a = 1, A = 1, d = 1;
// number of repeating characters starting at the corresponding position in the string
char[] carr = s.toCharArray();
int[] arr = new int[carr.length];
for (int i = 0; i < arr.length;) {
char currentChar = carr[i];
if (Character.isLowerCase(currentChar)) a = 0;
if (Character.isUpperCase(currentChar)) A = 0;
if (Character.isDigit(currentChar)) d = 0;
int j = i;
while (i < carr.length && carr[i] == carr[j]) {
i++;
}
arr[j] = i - j;
}
int totalMissing = (a + A + d);
// pad length to at least 6
if (arr.length < 6) {
result += totalMissing + Math.max(0, 6 - (arr.length + totalMissing));
} else {
int overLenCharNum = Math.max(arr.length - 20, 0), leftOverNum = 0;
result += overLenCharNum;
for (int k = 1; k < 3; k++) {
for (int i = 0; i < arr.length && overLenCharNum > 0; i++) {
if (arr[i] < 3 || arr[i] % 3 != (k - 1)) {
continue;
}
// turn all numbers in the arr array greater than 2 into the form of (3m + 2)
arr[i] -= Math.min(overLenCharNum, k);
overLenCharNum -= k;
}
}
for (int i = 0; i < arr.length; i++) {
if (arr[i] >= 3 && overLenCharNum > 0) {
// minimum number of changes needed to fix the repeating character problem by deletion only
int need = arr[i] - 2;
arr[i] -= overLenCharNum;
overLenCharNum -= need;
}
if (arr[i] >= 3) {
leftOverNum += arr[i] / 3;
}
}
result += Math.max(totalMissing, leftOverNum);
}
return result;
}
}

421. Maximum XOR of Two Numbers in an Array#

https://leetcode.com/problems/maximum-xor-of-two-numbers-in-an-array/


使用循环,设定前i位为可能的最大异或值,之后从set中寻找能匹配的记录。

class Solution {
public int findMaximumXOR(int[] nums) {
int max = 0, mask = 0;
// find max number to get max length
int maxNum = nums[0];
for (int i = 1; i < nums.length; i++) {
maxNum = Math.max(maxNum, nums[i]);
}
int len = Integer.toBinaryString(maxNum).length();
for (int i = len - 1; i >= 0; i--) {
// The mask will grow like 100..000 , 110..000, 111..000, then 1111...111
mask = mask | (1 << i);
HashSet<Integer> set = new HashSet<>();
// only care about the left parts
for (int num : nums) {
set.add(num & mask);
}
// try to find a candidate which can give the greediest try;
int tmp = max | (1 << i);
for (int prefix : set) {
// if a ^ b = c, then a ^ c = b
// to get b, we need a ^ c, if a ^ c exisited in our set, then we're good to go
if (set.contains(tmp ^ prefix)) {
max = tmp;
break;
}
}
}
return max;
}
}

422. Valid Word Square#

https://leetcode.com/problems/valid-word-square/


words中每个word提取出来,拷贝到二维数组matrix中,之后直接在matrix根据对角线镜像对称判断是否合法。

class Solution {
public boolean validWordSquare(List<String> words) {
if (words == null || words.isEmpty()) {
return false;
}
int m = words.size();
char[][] matrix = new char[m][m];
for (int i = 0; i < m; i++) {
String word = words.get(i);
if (word.length() > m) {
return false;
}
System.arraycopy(word.toCharArray(), 0, matrix[i], 0, word.length());
}
for (int row = 0; row < m; row++) {
for (int col = row; col < m; col++) {
if (matrix[row][col] != matrix[col][row]) {
return false;
}
}
}
return true;
}
}

423. Reconstruct Original Digits from English#

https://leetcode.com/problems/reconstruct-original-digits-from-english/


所有偶数英文单词都有一个独有的字母;而奇数的单词中虽然没有独有字母,但可以通过排除掉偶数之后得到唯一的情况。

class Solution {
public String originalDigits(String s) {
// hashmap, map letter into its frequency
char[] count = new char[26 + (int)'a'];
for (char letter: s.toCharArray()) {
count[letter]++;
}
// map array, map digit into its frequency
int[] out = new int[10];
// letter "z" only in "zero"
out[0] = count['z'];
// letter "w" only in "two"
out[2] = count['w'];
// letter "u" only in "four"
out[4] = count['u'];
// letter "x" only in "six"
out[6] = count['x'];
// letter "g" only in "eight"
out[8] = count['g'];
// letter "h" only in "three" and "eight"
out[3] = count['h'] - out[8];
// letter "f" only in "five" and "four"
out[5] = count['f'] - out[4];
// letter "s" only in "seven" and "six"
out[7] = count['s'] - out[6];
// letter "i" is in "nine", "five", "six", and "eight"
out[9] = count['i'] - out[5] - out[6] - out[8];
// letter "n" is in "one", "nine", and "seven"
out[1] = count['n'] - out[7] - 2 * out[9];
StringBuilder result = new StringBuilder();
for (int i = 0; i < 10; i++) {
for (int j = 0; j < out[i]; j++) {
result.append(i);
}
}
return result.toString();
}
}

424. Longest Repeating Character Replacement#

https://leetcode.com/problems/longest-repeating-character-replacement/


使用滑动窗口,用一个变量maxCount记录当前窗口中出现次数最多的单个字符数量。因为最多只能进行k次替换,因此必须保证当前窗口内的其他字符不能超过k个,否则就必须从窗口头部删去字符。每完成一次窗口的更新,就更新当前窗口的最大长度。

class Solution {
public int characterReplacement(String s, int k) {
int maxCount = 0; // largest count of a single, unique character in the current window
int maxLength = 0;
int[] freq = new int[26];
char[] chars = s.toCharArray();
for (int start = 0, end = 0; end < s.length(); end++) {
maxCount = Math.max(maxCount, ++freq[chars[end] - 'A']);
// at most k replacements in the window, and there are more characters in the window than we can replace
if (end - start + 1 - maxCount > k) {
freq[chars[start] - 'A']--;
start++;
}
maxLength = Math.max(maxLength, end - start + 1);
}
return maxLength;
}
}

425. Word Squares#

https://leetcode.com/problems/word-squares/


构造Trie,把输入的词都添加到TrieNode中,再构建一个len*len大小的char map用于DFS检查。注意DFS开始时传入的参数是一个TrieNode的数组tns,表示一共组建了n个Trie,tns[i]在DFS中表示对于word[i],下一个可用的字母。每一层DFS中都新开一个数组,拷贝符合的字母树路径。遍历当前的candidate字符串时,根据对称性修改二维char map的值进行匹配比较。如果纵向每个字母都能匹配就开始下一层递归,直到len层长度都匹配完成。

class Solution {
class TrieNode {
List<String> list; // all words that have this node as prefix
TrieNode[] children;
TrieNode() {
list = new ArrayList<>();
children = new TrieNode[26];
}
}
// O(n^len) * len
public List<List<String>> wordSquares(String[] words) {
TrieNode root = new TrieNode();
for (String w : words) {
root.list.add(w);
TrieNode tn = root;
for (char ch : w.toCharArray()) {
if (tn.children[ch - 'a'] == null) {
tn.children[ch - 'a'] = new TrieNode();
}
tn = tn.children[ch - 'a'];
// add word to each node in the tree
tn.list.add(w);
}
}
// length of square
int len = words[0].length();
// form n tries starting from root, one trie for each word
// tns[i] tells us in the DFS, for word[i], which letter is available next
TrieNode[] tns = new TrieNode[len];
Arrays.fill(tns, root);
List<List<String>> result = new ArrayList<>();
dfs(0, new char[len][len], result, tns);
return result;
}
private void dfs(int idx, char[][] m, List<List<String>> result, TrieNode[] tns) {
// one word square, add all the strings to result
if (idx == m.length) {
List<String> wordSquare = new ArrayList<>(idx);
for (char[] ca : m) {
wordSquare.add(new String(ca));
}
result.add(wordSquare);
return;
}
TrieNode[] tns2 = new TrieNode[tns.length];
//for each word in prefix list of this idx node, recursively build the word square starting from cand
for (String cand : tns[idx].list) {
int i = idx;
// set m[idx][i] to be cand[idx] the first time in this for loop
// for every after iteration i, set m[idx][i] and m[i][idx] to be cand[idx+i]
// check if one of the tns2 possible roots contain that ch, otherwise backtrack from dfs to try another candidate word
for (; i < m.length; ++i) {
char ch = cand.charAt(i);
m[idx][i] = ch;
if (i > idx) {
if (tns[i].children[ch - 'a'] == null) {
break;
}
m[i][idx] = ch;
//tns2 serves as the trie node for each position i that we are trying to fill
tns2[i] = tns[i].children[ch - 'a'];
}
}
// found a match for every letter in this cand word, then dfs at next level
if (i == m.length) {
dfs(idx + 1, m, result, tns2);
}
}
}
}

426. Convert Binary Search Tree to Sorted Doubly Linked List#

https://leetcode.com/problems/convert-binary-search-tree-to-sorted-doubly-linked-list


解题流程分为2步:

  1. 用中序遍历,把每一个BST节点之间用双向指针连起来
  2. 把头尾节点用双向指针连起来
class Solution {
Node curr = null;
public Node treeToDoublyList(Node root) {
if (root == null) {
return root;
}
Node dummyHead = new Node(0);
curr = dummyHead;
// inorder tranversal by recursion to connect the original BST
inOrderConnect(root);
// connect head and tail, notice that current ptr is tail
curr.right = dummyHead.right;
dummyHead.right.left = curr;
return dummyHead.right;
}
private void inOrderConnect(Node node) {
if (node == null) {
return;
}
inOrderConnect(node.left);
// construct doubly link
curr.right = node;
node.left = curr;
curr = node;
inOrderConnect(node.right);
}
}

427. Construct Quad Tree#

https://leetcode.com/problems/construct-quad-tree/


深度优先搜索,在四个子区域内不断减半长度,得到的四个新的区域节点,如果这四个区域节点的isLeaf()都返回true,并且节点的val都一致,说明当前节点本身已经是一个子节点了。否则,当前节点就是这四个子节点的父节点。

/*
// Definition for a QuadTree node.
class Node {
public boolean val;
public boolean isLeaf;
public Node topLeft;
public Node topRight;
public Node bottomLeft;
public Node bottomRight;
public Node() {
this.val = false;
this.isLeaf = false;
this.topLeft = null;
this.topRight = null;
this.bottomLeft = null;
this.bottomRight = null;
}
public Node(boolean val, boolean isLeaf) {
this.val = val;
this.isLeaf = isLeaf;
this.topLeft = null;
this.topRight = null;
this.bottomLeft = null;
this.bottomRight = null;
}
public Node(boolean val, boolean isLeaf, Node topLeft, Node topRight, Node bottomLeft, Node bottomRight) {
this.val = val;
this.isLeaf = isLeaf;
this.topLeft = topLeft;
this.topRight = topRight;
this.bottomLeft = bottomLeft;
this.bottomRight = bottomRight;
}
};
*/
class Solution {
public Node construct(int[][] grid) {
return helper(grid, 0, 0, grid.length);
}
private Node helper(int[][] grid, int x, int y, int len) {
// the minimum grid
if (len == 1) {
return new Node(grid[x][y] != 0, true);
}
Node result = new Node();
Node topLeft = helper(grid, x, y, len / 2);
Node topRight = helper(grid, x, y + len / 2, len / 2);
Node bottomLeft = helper(grid, x + len / 2, y, len / 2);
Node bottomRight = helper(grid, x + len / 2, y + len / 2, len / 2);
if (topLeft.isLeaf && topRight.isLeaf && bottomLeft.isLeaf && bottomRight.isLeaf
&& topLeft.val == topRight.val && topRight.val == bottomLeft.val && bottomLeft.val == bottomRight.val) {
// current grid has the same value
result.isLeaf = true;
result.val = topLeft.val;
} else {
// up to parent node
result.topLeft = topLeft;
result.topRight = topRight;
result.bottomLeft = bottomLeft;
result.bottomRight = bottomRight;
}
return result;
}
}

428. Serialize and Deserialize N-ary Tree#

https://leetcode.com/problems/serialize-and-deserialize-n-ary-tree/


核心是使用前序遍历法。序列化时,按照前序遍历顺序,每一个节点提取出val和子节点的个数进行转化,通过 + '0'的操作保证转化后这两个值能用1个字符表示(这里的前提是valN值都不会很大,例如都是200以内的数)。反序列化时,同样按照前序遍历,把字符转化为具体的值。可以使用一个变量index[1]记录当前处理序列化字符串的下标。

/*
// Definition for a Node.
class Node {
public int val;
public List<Node> children;
public Node() {}
public Node(int _val) {
val = _val;
}
public Node(int _val, List<Node> _children) {
val = _val;
children = _children;
}
};
*/
class Codec {
// Encodes a tree to a single string.
public String serialize(Node root) {
StringBuilder sb = new StringBuilder();
serialize(root, sb);
return sb.toString();
}
private void serialize(Node node, StringBuilder sb) {
if (node == null) {
return;
}
// append value char
sb.append((char) (node.val + '0'));
// append children size char
sb.append((char) (node.children.size() + '0'));
for (Node n : node.children) {
serialize(n, sb);
}
}
// Decodes your encoded data to tree.
public Node deserialize(String data) {
if (data == null || data.length() == 0) {
return null;
}
return deserialize(data, new int[1]);
}
Node deserialize(String data, int[] index) {
int val = data.charAt(index[0]) - '0';
int childrenSize = data.charAt(++index[0]) - '0';
Node node = new Node(val, new ArrayList<Node>());
for (int i = 0; i < childrenSize; i++) {
index[0]++;
node.children.add(deserialize(data, index));
}
return node;
}
}

429. N-ary Tree Level Order Traversal#

https://leetcode.com/problems/n-ary-tree-level-order-traversal/


与二叉树层次遍历类似,仍然是使用队列+BFS的思路,队列弹出当前节点时,遍历把所有子节点添加到队列中即可。

/*
// Definition for a Node.
class Node {
public int val;
public List<Node> children;
public Node() {}
public Node(int _val) {
val = _val;
}
public Node(int _val, List<Node> _children) {
val = _val;
children = _children;
}
};
*/
class Solution {
public List<List<Integer>> levelOrder(Node root) {
List< List<Integer> > result = new ArrayList<>();
if (root == null) {
return result;
}
Queue<Node> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
List<Integer> curLevel = new ArrayList<>();
// these are all nodes in current level
int currentLevelSize = queue.size();
for (int i = 0; i < currentLevelSize; i++) {
Node cur = queue.poll();
curLevel.add(cur.val);
// add current node's children to queue
for (Node child : cur.children) {
queue.offer(child);
}
}
// finish a level, add current level's value to result
result.add(curLevel);
}
return result;
}
}

430. Flatten a Multilevel Doubly Linked List#

https://leetcode.com/problems/flatten-a-multilevel-doubly-linked-list/


建立一个栈,如果遇到某个节点存在child指针,就把这个节点的next指针存到栈里,以待日后重新返回这一层时不迷路。之后,把这个节点和child指针指向的节点双向连起来,再把child指针删掉。

/*
// Definition for a Node.
class Node {
public int val;
public Node prev;
public Node next;
public Node child;
};
*/
class Solution {
public Node flatten(Node head) {
if (head == null) {
return null;
}
Node dummyHead = new Node(0, null, head, null);
Node cur, prev = dummyHead;
// set a stack and push head into it
Stack<Node> stack = new Stack();
stack.push(head);
while (!stack.isEmpty()) {
cur = stack.pop();
// link previous and current node
prev.next = cur;
cur.prev = prev;
// push next node or child node
if (cur.next != null) stack.push(cur.next);
if (cur.child != null) {
stack.push(cur.child);
// unlink current node with child
cur.child = null;
}
// move pointers forward
prev = cur;
}
// link first node with dummyHead to make a valid doubly linked list
dummyHead.next.prev = null;
return dummyHead.next;
}
}

431. Encode N-ary Tree to Binary Tree#

https://leetcode.com/problems/encode-n-ary-tree-to-binary-tree/


N叉树转化成二叉树时,总是将当前节点的第一个子节点转化成左儿子,剩下的儿子递归组成左儿子的右子树。

/*
// Definition for a Node.
class Node {
public int val;
public List<Node> children;
public Node() {}
public Node(int _val) {
val = _val;
}
public Node(int _val, List<Node> _children) {
val = _val;
children = _children;
}
};
*/
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Codec {
// Encodes an n-ary tree to a binary tree.
public TreeNode encode(Node root) {
if (root == null) {
return null;
}
TreeNode result = new TreeNode(root.val);
// next level -> left
if (root.children.size() > 0) {
result.left = encode(root.children.get(0));
}
// same level -> right
TreeNode cur = result.left;
for (int i = 1; i < root.children.size(); i++) {
cur.right = encode(root.children.get(i));
cur = cur.right;
}
return result;
}
// Decodes your binary tree to an n-ary tree.
public Node decode(TreeNode root) {
if (root == null) {
return null;
}
Node result = new Node(root.val, new ArrayList<>());
// first child is root.left, all other children are on the right subtree
TreeNode cur = root.left;
while (cur != null) {
result.children.add(decode(cur));
cur = cur.right;
}
return result;
}
}

432. All O`one Data Structure#

https://leetcode.com/problems/all-oone-data-structure/


对于插入和删除操作,使用HashMap可以轻松达到O(1)复杂度。而在此基础上,要想实现取最值也是O(1)复杂度,还需要借助双向链表LinkedHashSet。人工维护一个按照value大小排序的链表,每个链表节点中包含一个LinkedHashSet,里面存储了所有value为当前链表节点值的key。每一次插入删除节点时,更新链表以及节点的LinkedHashSet。具体而言,需要时刻跟踪链表的headtail两个头尾节点:

  • 插入key时,检查这个节点是不是新的节点
    • 如果是,把headtail都指向这个节点,更新节点中的Set以及全局Map
    • 如果不是,这个key对应的节点value自增。由于发生了自增,需要把key从之前的节点Set中踢掉,然后用新value创建一个新节点,把新节点插到原来节点的后面,再把key插到新节点的key中去。最后更新全局Map
  • 删除key时,需要确认这个key是否存在,之后
    • 如果是头节点,直接更新headtail
    • 否则,由于发生了自减,把把key从原来节点Set中踢掉,然后用新value创建一个新节点,把新节点插到原来节点的前面,再把key插到新节点的key中去。最后更新全局Map
  • 获取最大最小值,直接从链表headtail节点中的Set中提取,调用迭代器方法即可
class AllOne {
Map<String, Node> map;
Node head, tail;
class Node {
int val;
LinkedHashSet<String> keys;
Node next, pre;
public Node(int val) {
this.val = val;
keys = new LinkedHashSet();
next = pre = null;
}
}
/** Initialize your data structure here. */
public AllOne() {
map = new HashMap();
head = tail = null;
}
/** Inserts a new key <Key> with value 1. Or increments an existing key by 1. */
public void inc(String key) {
if (map.containsKey(key)) {
// get old node
Node node = map.get(key);
int val = node.val;
// remove old key in the old node
node.keys.remove(key);
// add new node with new val
if (node.next == null || node.next.val != val + 1) {
Node newNode = new Node(val + 1);
newNode.pre = node;
newNode.next = node.next;
node.next = newNode;
if (newNode.next != null) {
newNode.next.pre = newNode;
}
}
// add new key to the next node
node.next.keys.add(key);
// update node
map.put(key, node.next);
// update head
if (node.keys.isEmpty()) {
if (node == head) {
head = node.next;
}
node.next.pre = node.pre;
if (node.pre != null) {
node.pre.next = node.next;
}
}
// update tail
if (tail == node) {
tail = node.next;
}
} else {
if (head == null || head.val > 1) {
Node node = new Node(1);
node.next = head;
if (head != null) {
head.pre = node;
}
head = node;
if (tail == null) {
tail = node;
}
}
head.keys.add(key);
map.put(key, head);
}
}
/** Decrements an existing key by 1. If Key's value is 1, remove it from the data structure. */
public void dec(String key) {
if (!map.containsKey(key)) {
return;
}
// get old node
Node node = map.get(key);
int val = node.val;
// remove old val from old node
node.keys.remove(key);
// remove old key from map
map.remove(key);
// if it's head, update head and tail
if (val == 1) {
if (node.keys.isEmpty()) {
if (head == node) {
head = node.next;
}
if (head != null) {
head.pre = null;
}
if (tail == node) {
tail = null;
}
}
} else {
// add new node at pre position
if (node.pre == null || node.pre.val != val - 1) {
Node newNode = new Node(val - 1);
newNode.next = node;
newNode.pre = node.pre;
node.pre = newNode;
if (newNode.pre != null) {
newNode.pre.next = newNode;
}
}
// add new key to the newNode or pre node
node.pre.keys.add(key);
// update node
map.put(key, node.pre);
// update tail
if (node.keys.isEmpty()) {
if (tail == node) {
tail = node.pre;
}
node.pre.next = node.next;
if (node.next != null) {
node.next.pre = node.pre;
}
}
// update head
if (head == node) {
head = node.pre;
}
}
}
/** Returns one of the keys with maximal value. */
public String getMaxKey() {
return tail == null ? "" : tail.keys.iterator().next();
}
/** Returns one of the keys with Minimal value. */
public String getMinKey() {
return head == null ? "" : head.keys.iterator().next();
}
}
/**
* Your AllOne object will be instantiated and called as such:
* AllOne obj = new AllOne();
* obj.inc(key);
* obj.dec(key);
* String param_3 = obj.getMaxKey();
* String param_4 = obj.getMinKey();
*/

433. Minimum Genetic Mutation#

https://leetcode.com/problems/minimum-genetic-mutation/


使用回溯算法,遍历bank,每一次mutate一个字母,看是否能得到end;如果不能,退回尝试mutate其他字母。

class Solution {
private int count = Integer.MAX_VALUE;
public int minMutation(String start, String end, String[] bank) {
recurse(start, end, bank, 0, new HashSet<String>());
return count == Integer.MAX_VALUE ? -1 : count;
}
private void recurse(String start, String end, String[] bank, int mutateNum, Set<String> visited) {
if (start.equals(end)) {
count = Math.min(count, mutateNum);
}
for (String e : bank) {
int diff = 0;
// only 1 diff is allowed to mutate to the next state
for (int i = 0; i < e.length(); i++) {
if (start.charAt(i) != e.charAt(i)) {
diff++;
if (diff > 1) {
break;
}
}
}
// backtrack, try to mutate to next state then come back
if (diff == 1 && !visited.contains(e)) {
visited.add(e);
recurse(e, end, bank, mutateNum + 1, visited);
visited.remove(e);
}
}
}
}

434. Number of Segments in a String#

https://leetcode.com/problems/number-of-segments-in-a-string/


去掉空白部分,再分割字符串。

class Solution {
public int countSegments(String s) {
s = s.trim();
if (s == null || s.length() == 0) {
return 0;
}
String[] result = s.split("\\s+");
return result.length;
}
}

435. Non-overlapping Intervals#

https://leetcode.com/problems/non-overlapping-intervals/


按照每个区间的尾元素,从小到大排序。之后遍历所有区间,如果相邻两个区间不重叠,就把不重叠的区间个数加1。最后将所有区间个数减去不重叠的区间个数,得到需要移除的区间个数。

class Solution {
public int eraseOverlapIntervals(int[][] intervals) {
if (intervals == null || intervals.length == 0 || intervals[0].length == 0) {
return 0;
}
// sort by end element
Arrays.sort(intervals, (int[] a, int[] b) -> { return a[1] - b[1]; });
int end = intervals[0][1];
int nonOverlap = 1;
for (int i = 1; i < intervals.length; i++) {
if (intervals[i][0] >= end) {
end = intervals[i][1];
nonOverlap++;
}
}
return intervals.length - nonOverlap;
}
}

436. Find Right Interval#

https://leetcode.com/problems/find-right-interval/


使用桶,找出最小的start和最大的end,确定桶的大小。因为每个区间的start是唯一的,因此能够根据每个区间的start值,判断应该丢在哪个桶内。处理之后,每个桶里存放的就是下一个区间的起始值。注意部分桶可能为空,因此需要用相邻非空桶里的值覆盖。

class Solution {
public int[] findRightInterval(int[][] intervals) {
if (intervals == null || intervals.length == 0 || intervals[0].length == 0) {
return new int[] {};
}
int[] result = new int[intervals.length];
// find min start and max end
int min = intervals[0][0], max = intervals[0][1];
for (int i = 1 ; i < intervals.length; i++) {
min = Math.min(min, intervals[i][0]);
max = Math.max(max, intervals[i][1]);
}
int[] buckets = new int[max - min + 1];
Arrays.fill(buckets, -1);
for (int i = 0; i < intervals.length; i++) {
buckets[intervals[i][0] - min] = i;
}
for (int i = buckets.length - 2; i >= 0; i--) {
if (buckets[i] == -1) {
buckets[i] = buckets[i+1];
}
}
for (int i = 0; i < intervals.length; i++) {
result[i] = buckets[intervals[i][1] - min];
}
return result;
}
}

437. Path Sum III#

https://leetcode.com/problems/path-sum-iii/


使用一个Hashmap,统计每一条path sum以及对应出现的次数。遍历每一个节点的时候,可能出现的path sum由三部分组成:

  1. 以某个上层节点起始,当前节点结束的路径
  2. 左子树中的路径
  3. 右子树中的路径

在每完成一次递归调用返回上层时,需要注意把map中当前path sum对应的出现次数减少1。(可以理解为,通过左子树累加出来的次数,并不能对右子树产生效果,反之同理)

/**
* 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;
* }
* }
*/
class Solution {
public int pathSum(TreeNode root, int sum) {
if (root == null) {
return 0;
}
// <root to current node's sum, frequency>
Map<Long, Integer> map = new HashMap<>();
map.put(0L, 1);
return findPathSum(root, 0, sum, map);
}
private int findPathSum(TreeNode node, long sum, int target, Map<Long, Integer> map) {
if (node == null) {
return 0;
}
// update the prefix sum by adding the current node's val
sum += node.val;
// get the number of valid path, ended by the current node
int numPathToCurr = map.getOrDefault(sum - target, 0);
// update the map with the current sum
map.put(sum, map.getOrDefault(sum, 0) + 1);
// the total number of valid paths in the subtree rooted at the current node's left child
// the total number of valid paths in the subtree rooted at the current node's right child
// the number of valid paths ended by the current node
int result = numPathToCurr + findPathSum(node.left, sum, target, map) + findPathSum(node.right, sum, target, map);
// restore the map, as the recursion goes from the bottom to the top
map.put(sum, map.get(sum) - 1);
return result;
}
}

438. Find All Anagrams in a String#

https://leetcode.com/problems/find-all-anagrams-in-a-string/


使用双指针滑动窗口,用一个变量count统计频率为正的字母个数。移动滑动窗口时,如果count为0,说明窗口中能组成一个anagram。

class Solution {
public List<Integer> findAnagrams(String s, String p) {
List<Integer> result = new ArrayList<>();
if (s == null || p == null || s.length() == 0 || p.length() == 0) {
return result;
}
int[] arr = new int[26];
for (int i = 0; i < p.length(); i++) {
arr[p.charAt(i) - 'a']++;
}
int left = 0, right = 0;
int count = p.length();
while (right < s.length()) {
// character is existing in the map, update count and map
if (arr[s.charAt(right) - 'a'] >= 1) {
count--;
}
arr[s.charAt(right) - 'a']--;
right++;
// sum of all frequency >= 0, and count is the sum of all positive frequency
// so, every frequency is 0 if and only if count is 0
if (count == 0) {
result.add(left);
}
// window size is p's length, shrink the window size from the begin
if (right - left == p.length()) {
// restore a freq, and only update if in the map
if (arr[s.charAt(left) - 'a'] >= 0) {
count++;
}
arr[s.charAt(left) - 'a']++;
left++;
}
}
return result;
}
}

439. Ternary Expression Parser#

https://leetcode.com/problems/ternary-expression-parser/


统计?:的配对情况,遍历时如果遇到的?:数量相同,就检查?之前是T还是F,并根据相应true或false情况,截取对应的子字符串进行递归检查。

class Solution {
public String parseTernary(String expression) {
for (int i = 0; i < expression.length(); i++) {
if (expression.charAt(i) == '?') {
int flag = 1;
for (int j = i + 1; j < expression.length(); j++) {
if (expression.charAt(j) == '?') {
flag++;
}
if (expression.charAt(j) == ':') {
flag--;
}
// ? and : are equal now
if (flag == 0) {
if (expression.charAt(i-1) == 'T') {
return parseTernary(expression.substring(i+1, j));
} else {
return parseTernary(expression.substring(j+1, expression.length()));
}
}
}
}
}
return expression;
}
}

440. K-th Smallest in Lexicographical Order#

https://leetcode.com/problems/k-th-smallest-in-lexicographical-order/


可以把起始的1~9看成9棵树的根节点。对于根节点1,它有10个儿子,分别是10, 11, 12, … 19,而这10个子节点分别又可以有10个子节点,例如节点10有儿子100, 101, 102…以此类推,只要没有超过范围n,每个节点都可以有10个子节点,因此问题也就转化为了十叉树的先后遍历问题。

要确定每棵树的大小,其实就是确定以当前节点为根,子树中节点中的个数。观察发现,对于节点i,子节点中最小的一个节点一定是10*i。通过逐层*10,能够确定每一层的节点大小范围,进而确定每一层的节点个数,由此得到树的大小。之后,将树的大小与k进行判断,如果树的大小小于k,说明目标值并不在当前的树中,直接跳过;否则,才继续递归查找子树。

class Solution {
public int findKthNumber(int n, int k) {
long cur = 1;
while (k > 1) {
long count = findSubTreeCount(cur, n);
if (count <= k - 1) {
// travel to next tree
k -= count;
cur = cur + 1;
} else {
// travel to leftmost child
cur = cur * 10;
k -= 1;
}
}
return (int)cur;
}
private long findSubTreeCount(long cur, int n) {
long count = 0;
long upper = cur + 1;
while (cur <= n) {
// number of nodes in this level
count += Math.min(n + 1, upper) - cur;
cur = cur * 10;
upper = upper * 10;
}
return count;
}
}

441. Arranging Coins#

https://leetcode.com/problems/arranging-coins/


解方程即可,注意考虑溢出问题。

class Solution {
public int arrangeCoins(int n) {
return (int)((-1 + Math.sqrt(1 + 8 * (long)n)) / 2);
}
}

442. Find All Duplicates in an Array#

https://leetcode.com/problems/find-all-duplicates-in-an-array/


由于数组中的数范围都在1到n之间,因此可以遍历数组做一个映射,遇到数字num的时候,把nums[abs(num)] - 1对应的数翻转成负数。在翻转前进行判断,如果已经是负数了,说明之前遇到过num,这就是一个重复数。

class Solution {
public List<Integer> findDuplicates(int[] nums) {
List<Integer> result = new ArrayList<>();
for (int i = 0; i < nums.length; i++) {
int index = Math.abs(nums[i]) - 1;
if (nums[index] < 0) {
result.add(index + 1);
}
nums[index] = -nums[index];
}
return result;
}
}

443. String Compression#

https://leetcode.com/problems/string-compression/


由于限定只能用O(1)的空间,因此只能用双指针遍历输入字符数组,右边的处理每个字符,左边的负责原地修改数组内容。解题流程分为3步:

  1. 遇到相同的字符就用循环跳过,并统计出现的相同字符个数,直到遇上下一个不同的字符
  2. 把当前处理的字符拷贝到数组左侧
  3. 如果当前的字符被压缩,把重复的个数转化成字符串,添加到字符后面
class Solution {
public int compress(char[] chars) {
int result = 0, index = 0;
while (index < chars.length) {
char currentChar = chars[index];
int count = 0;
// compress same character
while (index < chars.length && chars[index] == currentChar) {
index++;
count++;
}
// add current char to array
chars[result] = currentChar;
result++;
// convert count to string format then add
if (count != 1) {
for (char c : Integer.toString(count).toCharArray()) {
chars[result] = c;
result++;
}
}
}
return result;
}
}

444. Sequence Reconstruction#

https://leetcode.com/problems/sequence-reconstruction/


使用一个数组index记录下nums中每个元素的下标。之后遍历seqs中的每个list,进行如下检查:

  1. list中的每个数必须在[1, n]的范围内
  2. 任意list中,前一个数在nums中的下标必须小于后一个数在nums中的下标,保证拓扑序的一致性
  3. 如果前一个数在nums中的下标 + 1为后一个数在nums中的下标,便能够保证这两个数的拓扑序的唯一性。必须保证所有相邻两数之间都能满足这一拓扑序的唯一性,才能最终覆盖序列的所有数
class Solution {
public boolean sequenceReconstruction(int[] nums, List<List<Integer>> seqs) {
if (seqs == null || seqs.size() == 0) {
return false;
}
int len = nums.length;
boolean[] covered = new boolean[len + 1];
// for each element in nums, record its index
int[] index = new int[len + 1];
for (int i = 0; i < len; i++) {
index[nums[i]] = i;
}
for (List<Integer> list : seqs) {
for (int i = 0; i < list.size(); i++) {
int cur = list.get(i);
// each number should be in the range of [1, n]
if (cur < 1 || cur > nums.length) {
return false;
}
// previous element must appear before current element in nums
if (i == 0) {
continue;
}
int prev = list.get(i - 1);
if (index[prev] >= index[cur]) {
return false;
}
// ensure one-by-one order in index
if (!covered[prev] && index[prev] + 1 == index[cur]) {
covered[prev] = true;
}
}
}
// must cover all numbers
for (int i = 0; i < nums.length - 1; i++) {
if (covered[nums[i]] == false) {
return false;
}
}
return true;
}
}

445. Add Two Numbers II#

https://leetcode.com/problems/add-two-numbers-ii/


由于限制不能反转链表,因此只能从高位开始按顺序递归累加。先提前获取两个链表的长度,确保能先从长的链表开始遍历,每完成一次遍历,更新两个链表的长度差,判断此时长链表是否与短链表的最高位齐平。遍历的时候有两种情况:

  • 单独遍历长链表节点时,节点的值由长链表节点的值 + 进位组成。
  • 同时遍历双链表节点时,节点的值由两个链表节点的值 + 进位组成

当前位调用递归的时候,能够获取后面所有位数的结果post,并同时携带了进位信息。注意,进位信息保存在post的最前一个节点,并不需要额外用一个变量储存。同理,全部递归完成后,处理第一个节点的进位也是用相同的方法。

/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
int size1 = getListLength(l1);
int size2 = getListLength(l2);
ListNode head = new ListNode(1);
// make sure l1.length >= l2.length
head.next = size1 < size2 ? helper(l2, l1, size2 - size1) : helper(l1, l2, size1 - size2);
// handle the first digit
if (head.next.val > 9) {
head.next.val = head.next.val % 10;
return head;
}
return head.next;
}
public int getListLength(ListNode l) {
int len = 0;
while (l != null) {
l = l.next;
len++;
}
return len;
}
public ListNode helper(ListNode l1, ListNode l2, int lenDiff) {
if (l1 == null) {
return null;
}
ListNode current = lenDiff == 0 ? new ListNode(l1.val + l2.val) : new ListNode(l1.val);
ListNode post = lenDiff == 0 ? helper(l1.next, l2.next, 0) : helper(l1.next, l2, lenDiff - 1);
// handle carry
if (post != null && post.val > 9) {
current.val += 1;
post.val = post.val % 10;
}
current.next = post;
return current;
}
}

446. Arithmetic Slices II - Subsequence#

https://leetcode.com/problems/arithmetic-slices-ii-subsequence/


使用二维dp,其中dp[i][j]表示包含dp[i]dp[j]的arithmetic序列个数。遍历数组,记录每一个数出现的下标,并把所有下标归类到一个链表中,再把<下标, 链表>映射以HashMap形式存储。之后双重循环,锁定ij,利用A[k] - A[i] = A[i] - A[j] (k < i < j)这个性质,从HashMap中查找是否存在合适的k值。如果存在,dp[i][j]值的就在dp[k][i]的基础上 + 1,表示新增了一个arithmetic序列。每次完成循环后,当前dp[i][j]新加的值就统计到总的结果中。

class Solution {
public int numberOfArithmeticSlices(int[] nums) {
int n = nums.length;
int[][] dp = new int[n][n];
Map<Long, List<Integer>> map = new HashMap();
for (int i = 0; i < n; i++) {
map.putIfAbsent((long)nums[i], new ArrayList());
map.get((long)nums[i]).add(i);
}
int result = 0;
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
long target = 2 * (long) nums[i] - nums[j];
if (map.containsKey(target)) {
for (int k : map.get(target)) {
if (k < i) {
dp[i][j] += (dp[k][i] + 1);
}
}
}
result += dp[i][j];
}
}
return result;
}
}

447. Number of Boomerangs#

https://leetcode.com/problems/number-of-boomerangs/


双重循环,两两判断点之间点的距离,固定i并得到当前points[i]points[j]之间的距离,通过hashmap.merge()方法得到这一轮i循环中这个距离出现的次数,如果已经出现了distCount次,组合数增加就要增加distCount - 1(相当于在加入当前这段距离之前,原来的重复距离每一个都要跟新加入的距离组成配对)。由于每个组合内后两个点顺序可以互换,因此最后总的结果还要乘2。

注意,这里的hashmap.merge()方法作用是依照BiFunction,替换更新key对应的value。在本题中,其时间复杂度是O(1),因为BiFunction是两数求和,而其余的合并底层操作,比如get和remove等也都是O(1)。

class Solution {
public int numberOfBoomerangs(int[][] points) {
int result = 0;
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < points.length; i++) {
for (int j = 0; j < points.length; j++) {
if (i == j) {
continue;
}
// count of current distance between two points
int distCount = map.merge(getDist(points[i], points[j]), 1, Integer::sum);
// shake hand rules
result += distCount - 1;
}
map.clear();
}
// double order, double result
return result * 2;
}
public int getDist(int a[], int b[]) {
return (a[0] - b[0])*(a[0] - b[0]) + (a[1] - b[1])*(a[1] - b[1]);
}
}

448. Find All Numbers Disappeared in an Array#

https://leetcode.com/problems/find-all-numbers-disappeared-in-an-array/


两次循环,第一次循环访问每个数对应的下标,把下标位置上对应的数加上n。第二次循环直接顺序检查每个下标,如果当前下标对应的值小于n,说明之前并没有被访问修改过,因此说明数组中并不存在对应的数字来跳转访问这个下标。

class Solution {
public List<Integer> findDisappearedNumbers(int[] nums) {
List<Integer> result = new ArrayList<>();
int n = nums.length;
// modify original number and add n
for (int i = 0; i < n; i++) {
nums[(nums[i]-1) % n] += n;
}
// numbers still less than n are disappeared
for (int i = 0; i < n; i++) {
if (nums[i] <= n) {
result.add(i+1);
}
}
return result;
}
}

449. Serialize and Deserialize BST#

https://leetcode.com/problems/serialize-and-deserialize-bst/


依然是前序遍历编码解码的思想。由于要求编码的结果尽可能紧凑,可以将null的值压缩成n

/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
public class Codec {
// Encodes a tree to a single string.
public String serialize(TreeNode root) {
StringBuilder sb = new StringBuilder();
serializeHelper(root, sb);
return sb.toString();
}
private void serializeHelper(TreeNode node, StringBuilder sb) {
if (node == null) {
sb.append("n,");
return;
}
sb.append(node.val).append(",");
serializeHelper(node.left, sb);
serializeHelper(node.right, sb);
}
// Decodes your encoded data to tree.
public TreeNode deserialize(String data) {
String[] nodes = data.split(",");
int[] index = new int[1];
return deserializeHelper(nodes, index);
}
private TreeNode deserializeHelper(String[] nodes, int[] index) {
if (nodes[index[0]].equals("n")) {
index[0]++;
return null;
}
TreeNode cur = new TreeNode(Integer.valueOf(nodes[index[0]]));
index[0]++;
cur.left = deserializeHelper(nodes, index);
cur.right = deserializeHelper(nodes, index);
return cur;
}
}
// Your Codec object will be instantiated and called as such:
// Codec ser = new Codec();
// Codec deser = new Codec();
// String tree = ser.serialize(root);
// TreeNode ans = deser.deserialize(tree);
// return ans;

450. Delete Node in a BST#

https://leetcode.com/problems/delete-node-in-a-bst/


找到目标节点,从右子树中找到最小的节点进行替换。

/**
* 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;
* }
* }
*/
class Solution {
public TreeNode deleteNode(TreeNode root, int key) {
if (root == null) {
return root;
}
// Search in subtrees
if (root.val < key) {
root.right = deleteNode(root.right, key);
return root;
} else if (root.val > key) {
root.left = deleteNode(root.left, key);
return root;
}
// No child or only one child
if (root.left == null && root.right == null) {
return null;
} else if (root.left == null) {
return root.right;
} else if (root.right == null) {
return root.left;
}
// Two children
if (root.right.left == null) {
root.right.left = root.left;
return root.right;
} else {
TreeNode smallest = deleteSmallest(root.right);
smallest.left = root.left;
smallest.right = root.right;
return smallest;
}
}
// Find the smallest node in right subtree and delete it
private TreeNode deleteSmallest(TreeNode root) {
TreeNode cur = root.left;
TreeNode pre = root;
while (cur.left != null) {
pre = cur;
cur = cur.left;
}
pre.left = cur.right;
return cur;
}
}

451. Sort Characters By Frequency#

https://leetcode.com/problems/sort-characters-by-frequency/


存储<character, frequency>map序列并按照frequency降序排序,之后按照顺序输出每个字符即可。

class Solution {
public String frequencySort(String s) {
int[] map = new int[128];
for (char c : s.toCharArray()) {
map[c]++;
}
// insert <character, frequency> into a priority queue, with descending frequency order
PriorityQueue<int[]> q = new PriorityQueue<>((a, b) -> b[1] - a[1]);
// insert only if the char is present in input string
for (int i = 0; i < 128; i++) {
if (map[i] > 0) {
q.offer(new int[] {i, map[i]});
}
}
char[] result = new char[s.length()];
int index = 0;
// output characters from queue
while (!q.isEmpty()) {
int[] entry = q.poll();
char letter = (char) entry[0];
int frequency = entry[1];
for (int i = 0; i < frequency; i++) {
result[index] = letter;
index++;
}
}
return String.valueOf(result);
}
}

452. Minimum Number of Arrows to Burst Balloons#

https://leetcode.com/problems/minimum-number-of-arrows-to-burst-balloons/


本质上是区间合并问题的变种,先按照xend对每个区间进行排序,之后遍历,如果相邻两个区间没有重叠,就说明这两个区间一定需要2箭分别穿过,总的箭数量+1;反之说明两个区间可以合并,用1箭穿过。

class Solution {
public int findMinArrowShots(int[][] points) {
if (points == null || points.length == 0) {
return 0;
}
// sort by x-end coordinate
Arrays.sort(points, (a, b) -> { return a[1] == b[1] ? 0 : a[1] > b[1] ? 1 : -1; });
int result = 1;
int curEnd = points[0][1];
for (int i = 1; i < points.length;) {
// need another arrow to shoot
if (points[i][0] > curEnd) {
curEnd = points[i][1];
result++;
} else {
// two balloons can be shot with one arrow, merge them
i++;
}
}
return result;
}
}

453. Minimum Moves to Equal Array Elements#

https://leetcode.com/problems/minimum-moves-to-equal-array-elements/


n - 1个数增大1,等同于1个数减少1。因此只要找出数组最小值,再计算其他每个数跟最小值的差值总和是多少即可。

class Solution {
public int minMoves(int[] nums) {
int result = 0;
// find minimum value of the array
int min = Integer.MAX_VALUE;
for (int n : nums) {
min = Math.min(min, n);
}
// n - 1 number increment by 1 = 1 number decrement by 1, and all numbers should match with minimum value
for (int i = 0; i < nums.length; i++) {
result += nums[i] - min;
}
return result;
}
}

454. 4Sum II#

https://leetcode.com/problems/4sum-ii/


对4sum进行拆分,先算出nums1nums2中两两组合能得到的所有sum,并统计每个sum出现的个数,把结果存到map中。再遍历nums3nums4中所有的两两组合,如果发现map中有对应的key(负值),说明当前4个数字能组成4sum为0的组合。

class Solution {
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
if (nums1 == null || nums1.length == 0 || nums2 == null || nums2.length == 0 ||
nums3 == null || nums3.length == 0 || nums4 == null || nums4.length == 0) {
return 0;
}
int result = 0;
int len = nums1.length;
// <sum of two numbers, count>
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < len; i++) {
for (int j = 0; j < len; j++) {
map.compute(nums1[i] + nums2[j], (k, v) -> v == null ? 1 : v + 1);
}
}
for (int i = 0; i < len; i++) {
for (int j = 0; j < len; j++) {
if (map.containsKey(-(nums3[i] + nums4[j]))) {
result += map.get(-(nums3[i] + nums4[j]));
}
}
}
return result;
}
}

455. Assign Cookies#

https://leetcode.com/problems/assign-cookies/


先对两个数组进行排序。遍历s,总是尝试用当前最小size的饼干去满足当前的小孩,如果能满足的话,小孩个数+1。无论是否成功满足,都必须遍历下一块饼干。

class Solution {
public int findContentChildren(int[] g, int[] s) {
Arrays.sort(g);
Arrays.sort(s);
int result = 0;
for (int i = 0; result < g.length && i < s.length; i++) {
// always try to use the minimum size to satisfy current child
if (g[result] <= s[i]) {
result++;
}
}
return result;
}
}

456. 132 Pattern#

https://leetcode.com/problems/132-pattern/


单调栈,从数组右侧开始遍历,如果当前数nums[i]比栈顶的元素更大,就把栈顶所有小于等于nums[i]的数弹出,弹出的数作为s3 candidate,而栈顶作为s2 candidate。这一步的目的是确保s2 > s3。在这个前提下,如果又能在遍历时发现nums[i] < s3,则说明nums[i]是合适的s1 candidate。

class Solution {
public boolean find132pattern(int[] nums) {
int s3 = Integer.MIN_VALUE;
Stack<Integer> stack = new Stack<Integer>();
for (int i = nums.length - 1; i >= 0; i--) {
if (nums[i] < s3) {
// num[i] is s1 candidate
return true;
} else {
// pop out all stack top numbers that are smaller than nums[i]. Left numbers are s2 candidates
while (!stack.isEmpty() && nums[i] > stack.peek()) {
s3 = stack.pop();
}
stack.push(nums[i]);
}
}
return false;
}
}

457. Circular Array Loop#

https://leetcode.com/problems/circular-array-loop/


用DFS,维护每个元素的访问状态,转化为DFS检测环问题。注意需排除长度为1,以及包含前后movement的情况。

class Solution {
private int NOT_VISITED = 0;
private int VISITING = 1;
private int VISITED = 2;
public boolean circularArrayLoop(int[] nums) {
int N = nums.length;
int[] visited = new int[N];
for (int i = 0; i < N; i++) {
if (visited[i] == NOT_VISITED && dfs(i, visited, nums)) {
return true;
}
}
return false;
}
// return true if there is a cycle
private boolean dfs(int cur, int[] visited, int[] nums) {
int N = nums.length;
if (visited[cur] == VISITING) {
return true;
}
if (visited[cur] == VISITED) {
return false;
}
visited[cur] = VISITING;
int next = cur + nums[cur];
next %= N;
if (next < 0) {
next += N;
}
// invalid cycle if the length is 1, or it consists of forward and backward movement
if (next == cur || nums[next] * nums[cur] < 0) {
visited[cur] = VISITED;
return false;
}
if (dfs(next, visited, nums)) {
return true;
}
visited[cur] = VISITED;
return false;
}
}

458. Poor Pigs#

https://leetcode.com/problems/poor-pigs/


需要至少minutesToTest / minutesToDie + 1轮测试,每一轮测试中的每只猪都要能覆盖到一些bucket,必须要保证通过全部的测试,所有的bucket都被覆盖到。

class Solution {
public int poorPigs(int buckets, int minutesToDie, int minutesToTest) {
int result = 0;
while (Math.pow(minutesToTest / minutesToDie + 1, result) < buckets) {
result += 1;
}
return result;
}
}

459. Repeated Substring Pattern#

https://leetcode.com/problems/repeated-substring-pattern/


从输入字符串的中间开始,向前遍历到能整除字符串长度的下标,因为这个下标可能是重复pattern的结尾。之后双指针向前移动,确保双指针经过的内容完全一致,直到右指针到达字符串终点。

class Solution {
public boolean repeatedSubstringPattern(String s) {
char [] chars = s.toCharArray();
int len = chars.length;
for (int i = len / 2; i > 0; i--) {
// potential pattern start
if (len % i == 0) {
int left = 0;
int right = i;
// should always match
while (right < len && chars[left] == chars[right]) {
left++;
right++;
}
if (right == len) {
return true;
}
}
}
return false;
}
}

460. LFU Cache#

https://leetcode.com/problems/lfu-cache/


维护3个HashMap,第一个keyToVal用于存储每个输入的(key, value)对;第二个keyToCount用于存储每个key被使用了几次;第三个countToLRUKeys用于存储对于每个使用次数count,分别有哪些key被使用了这么多次,相同使用次数的key用一个HashSet存在一起。

put新的(key, value)时,首先确保capacity有效。之后讨论情况:

  • 如果当前key已经存在,则覆盖原来keyToVal保存的值,直接返回
  • 如果这个key不存在,并且已经没有空余位置,需要从当前使用次数最少的key中踢掉一个,注意踢的时候既要从countToLRUKeys,也要从keyToVal踢,踢完之后,更新keyToVal,并且注意此时的最小使用count一定是1(要么是新key,要么之前的最小值就是1),在这个基础上更新keyToCountcountToLRUKeys

get(key)时,先确保key在keyToVal中存在。之后从countToLRUKeys中移除掉自己的记录(因为自己的count + 1了),再看情况决定是否需要更新min。最后更新keyToCount,返回keyToVal的取值。

class LFUCache {
private int min;
private int capacity;
private HashMap<Integer, Integer> keyToVal;
private HashMap<Integer, Integer> keyToCount;
private HashMap<Integer, LinkedHashSet<Integer>> countToLRUKeys;
public LFUCache(int capacity) {
this.min = -1;
this.capacity = capacity;
this.keyToVal = new HashMap<>();
this.keyToCount = new HashMap<>();
this.countToLRUKeys = new HashMap<>();
}
public int get(int key) {
if (!keyToVal.containsKey(key)) {
return -1;
}
int count = keyToCount.get(key);
// remove key from current count (since we will increase count)
countToLRUKeys.get(count).remove(key);
// no element is of min count
if (count == min && countToLRUKeys.get(count).size() == 0) {
min++;
}
updateCount(key, count + 1);
return keyToVal.get(key);
}
public void put(int key, int value) {
if (capacity <= 0) {
return;
}
if (keyToVal.containsKey(key)) {
// update key's value
keyToVal.put(key, value);
// update key's count
get(key);
return;
}
// new element and no enough space, evict LRU from this min count bucket
if (keyToVal.size() >= capacity) {
int evictKey = countToLRUKeys.get(min).iterator().next();
countToLRUKeys.get(min).remove(evictKey);
keyToVal.remove(evictKey);
}
// adding new key and value
keyToVal.put(key, value);
// min count must be 1 now
min = 1;
// adding new key and min count
updateCount(key, min);
}
private void updateCount(int key, int count) {
keyToCount.put(key, count);
if (!countToLRUKeys.containsKey(count)) {
countToLRUKeys.put(count, new LinkedHashSet<>());
}
countToLRUKeys.get(count).add(key);
}
}
/**
* Your LFUCache object will be instantiated and called as such:
* LFUCache obj = new LFUCache(capacity);
* int param_1 = obj.get(key);
* obj.put(key,value);
*/

461. Hamming Distance#

https://leetcode.com/problems/hamming-distance/


要求出不同的位数,其实就是看xy异或结果中1的个数,可以使用循环右移法,累加出1的总数。

class Solution {
public int hammingDistance(int x, int y) {
int xor = x ^ y;
int result = 0;
for (int i = 0; i < 32; i++) {
result += (xor >> i) & 1;
}
return result;
}
}

462. Minimum Moves to Equal Array Elements II#

https://leetcode.com/problems/minimum-moves-to-equal-array-elements-ii/


将数组排序,让最大和最小的数组成一对,次大和次小的数组成一对,以此类推,累加每组数对的差值。

class Solution {
public int minMoves2(int[] nums) {
Arrays.sort(nums);
int count = 0;
int i = 0, j = nums.length - 1;
while (i < j) {
count += nums[j] - nums[i];
i++;
j--;
}
return count;
}
}

463. Island Perimeter#

https://leetcode.com/problems/island-perimeter/


检查每个方格,如果是岛,先加上4的边长;之后检查这个岛的左、上是否有相邻岛,如果有相邻就减2边长,因为相邻的两个岛都不需要交界边了。为了避免重复,检查相邻岛时只需要检查左、上即可(右、下也可以)。

class Solution {
public int islandPerimeter(int[][] grid) {
if (grid == null || grid.length == 0 || grid[0].length == 0) {
return 0;
}
int result = 0;
for (int i = 0; i < grid.length; i++) {
for (int j = 0; j < grid[0].length; j++) {
if (grid[i][j] == 1) {
result += 4;
if (i > 0 && grid[i-1][j] == 1) {
result -= 2;
}
if (j > 0 && grid[i][j-1] == 1) {
result -= 2;
}
}
}
}
return result;
}
}

464. Can I Win#

https://leetcode.com/problems/can-i-win/


排除掉一些特殊情况,比如desiredTotal太小或者太大,或者恰好二者相差为1,其余情况使用DFS,用二进制状态码表示当前已经选择的数字,并且再推导出选择下一个数字后的新状态,逐步深入判断先手玩家能否取胜,再修改对应的DP数组。

class Solution {
public boolean canIWin(int maxChoosableInteger, int desiredTotal) {
// first player directly win
if (maxChoosableInteger >= desiredTotal) {
return true;
} else if (desiredTotal <= maxChoosableInteger * 2 - 1) {
// if max + 1 = desired, first player choose x, second always choose max - x and win
return maxChoosableInteger + 1 != desiredTotal;
} else if (maxChoosableInteger * (maxChoosableInteger + 1) / 2 < desiredTotal) {
// desiredTotal is too large to reach
return false;
}
return dfs(maxChoosableInteger, 0, desiredTotal, new int[(1 << (maxChoosableInteger))]);
}
private boolean dfs(int maxChoosableInteger, int chosen, int remaining, int[] memo) {
if (memo[chosen] != 0) {
// win case
return memo[chosen] == 1;
}
for (int i = maxChoosableInteger; i >= 1; i--) {
// current state is chosen and we want to try number i, next state is chosen ^ (1 << i - 1).
int mask = 1 << (i-1);
if ((chosen & mask) != 0) {
continue;
}
if (i >= remaining) {
// reach or exceed desired total
return true;
}
// first player win
if (!dfs(maxChoosableInteger, chosen | mask, remaining - i, memo)) {
memo[chosen] = 1;
return true;
}
}
// second player win
memo[chosen] = -1;
return false;
}
}

465. Optimal Account Balancing#

https://leetcode.com/problems/optimal-account-balancing/


首先使用两个HashMap统计出每个人的净收入/支出情况。之后使用DFS进行回溯,找到第一个债务不是0的人固定为start,然后在此人后面找到另一个债务也不是0,并且极性与之相反的人,试图让这两个人完成交易互相清算一次债务。以此类推,之后的人按照这个流程,然后根据回溯判断出哪一种方案需要的交易次数最少。

如果要进一步加快运行速度,可以对一些特殊情况进行优化,在开始DFS之前先检查一遍,找出所有双人组,每个双人组中的一个人恰好需要偿还给另一个人所应得的债务。这种交易方式一定是最优的,因为它只需要最少的交易次数,就能让两个人的最终债务情况都变成0。

class Solution {
public int minTransfers(int[][] transactions) {
// track each person's net balance
HashMap<Integer, Integer> map = new HashMap();
for (int[] transaction : transactions) {
// Subtract from source
map.put(transaction[0], map.getOrDefault(transaction[0], 0) - transaction[2]);
// Add to target
map.put(transaction[1], map.getOrDefault(transaction[1], 0) + transaction[2]);
}
int[] balances = new int[map.size()];
int idx = 0;
for (int key : map.keySet()) {
if (map.get(key) != 0) {
balances[idx] = map.get(key);
idx++;
}
}
return dfs(0, balances);
}
public int dfs(int start, int[] balances) {
// find first non-zero.
while (start < balances.length && balances[start] == 0) {
start++;
}
// all debts are cleared
if (start == balances.length) {
return 0;
}
int min = Integer.MAX_VALUE;
// Avoid previous duplicate back-to-back balances. Default is 0, since we check non-zero balances anyway
int prev = 0;
// FIRST check all pairs of opposite balances that cancel each other out, before diving deep into DFS.
for (int i = start + 1; i < balances.length; i++) {
if (balances[start] + balances[i] == 0) {
balances[i] += balances[start];
min = Math.min(min, 1 + dfs(start + 1, balances));
balances[i] -= balances[start];
prev = balances[i];
// early termination, since this will be the best settling of debts
return min;
}
}
for (int i = start + 1; i < balances.length; i++) {
if (balances[i] != prev && balances[start] * balances[i] < 0) {
balances[i] += balances[start];
min = Math.min(min, 1 + dfs(start + 1, balances));
balances[i] -= balances[start];
prev = balances[i];
}
}
// If the min is still Integer.MAX_VALUE, we've settled all balances to 0, so the fewest transactions is now 0.
return min == Integer.MAX_VALUE ? 0 : min;
}
}

466. Count The Repetitions#

https://leetcode.com/problems/count-the-repetitions/


双指针法,移动idx1使得idx1idx2指向相同字符,分类讨论两种情况,要么是idx2走完了一个s2长度;要么是发现idx1走了一个完整的循环,并用这个idx2长度除以s2长度除以s2重复次数。

class Solution {
class Index {
int idx1;
int idx2;
public Index(int i1, int i2) {
idx1 = i1;
idx2 = i2;
}
}
public int getMaxRepetitions(String s1, int n1, String s2, int n2) {
HashMap<Integer, Index> idxMap = new HashMap();
int len1 = s1.length(), len2 = s2.length();
int idx1 = 0, idx2 = 0;
int totalCharsOf1 = len1 * n1;
while (idx1 < totalCharsOf1) {
int modIdx1 = idx1 % len1;
int modIdx2 = idx2 % len2;
if (s1.charAt(modIdx1) == s2.charAt(modIdx2)) {
int key = modIdx1 * 1000 + modIdx2;
if (idxMap.containsKey(key)) {
Index prev = idxMap.get(key);
int d1 = idx1 - prev.idx1;
int d2 = idx2 - prev.idx2;
int k = (totalCharsOf1 - idx1) / d1;
idx1 += k * d1;
idx2 += k * d2;
if (idx1 >= totalCharsOf1) {
break;
}
} else {
idxMap.put(key, new Index(idx1, idx2));
}
idx2++;
}
idx1++;
}
return idx2 / len2 / n2;
}
}

467. Unique Substrings in Wraparound String#

https://leetcode.com/problems/unique-substrings-in-wraparound-string/


使用一个map,用于统计以每个字母为开头的子字符串的个数。如果给定的原字符串是连续的,那么很显然每遍历一个字符,连续字符串的size就增加1,遍历n个字符之后以起始字符开头的子字符串就是n个,第二个字符开头的子字符串就是n-1个,以此类推。

完成遍历后,对于每个字母做一下比较,比较这个字母是作为一个子字符串头,能生成的数量多,还是作为一个已有连续子字符串的中间元素,能生成的数量多。注意因为是循环数组,需要循环两次,保证map中的每个元素都能被更新。

class Solution {
public int findSubstringInWraproundString(String p) {
if (p.length() == 0) {
return 0;
}
int[] map = new int[26];
char[] chs = p.toCharArray();
char head = chs[0];
int size = 0; // current consecutive substring length
for (int i = 0; i < chs.length; i++) {
if (i == 0 || isAdjacent(chs[i - 1], chs[i])) {
size++;
} else {
map[head - 'a'] = Math.max(map[head - 'a'], size);
size = 1;
head = chs[i];
}
}
// last consecutive substring
map[head - 'a'] = Math.max(map[head - 'a'], size);
// repeat loop to ensure to update all elements
for (int i = 0; i < 26; i++) {
map[i % 26] = Math.max(map[i % 26], map[(i + 25) % 26] - 1);
}
for (int i = 0; i < 26; i++) {
map[i % 26] = Math.max(map[i % 26], map[(i + 25) % 26] - 1);
}
int result = 0;
for (int num: map) {
result += num;
}
return result;
}
private boolean isAdjacent(char a, char b) {
return a + 1 == b || a == 'z' && b == 'a';
}
}

468. Validate IP Address#

https://leetcode.com/problems/validate-ip-address/


使用split()函数分割输入IP,之后进行检查:

  • 对于IPv4而言,每个部分需要是整数,不能大于255,如果是0长度必须为1;总共只能有4个整数
  • 对于IPv6而言,每个部分需要是整数,不能超过65535,长度不能超过4;总共只能有8个整数

注意输入的queryIP可能在结尾包含空字符串,需要在split()中把limit参数设为-1,这样不会忽略空字符串。

class Solution {
public String validIPAddress(String queryIP) {
if (isIPv4(queryIP)) {
return "IPv4";
} else if (isIPv6(queryIP)) {
return "IPv6";
}
return "Neither";
}
private boolean isIPv4(String ip) {
// trailing empty strings will not be trimmed
String[] arr = ip.split("\\.", -1);
for (String a: arr) {
try {
if (Integer.parseInt(a) > 255 || (a.charAt(0) == '0' && a.length() != 1)) {
return false;
}
} catch (NumberFormatException e) {
return false;
}
}
return arr.length == 4;
}
private boolean isIPv6(String ip) {
// trailing empty strings will not be trimmed
String[] arr = ip.split(":", -1);
for (String a: arr) {
try {
if (Integer.parseInt(a, 16) > 65535 || a.length() > 4) {
return false;
}
} catch (NumberFormatException e) {
return false;
}
}
return arr.length == 8;
}
}

469. Convex Polygon#

https://leetcode.com/problems/convex-polygon/


凸多边形任何一个内角不能超过180度,所以相邻三个点组成的两条相邻边,交点不能在多边形内部。

class Solution {
public boolean isConvex(List<List<Integer>> points) {
int flag = 0;
for (int i = 0; i < points.size(); i++) {
int angle = getAngle(points, i);
// collinear
if (angle == 0) {
continue;
}
if (flag == 0) {
flag = angle > 0 ? 1 : -1;
} else if (flag > 0 != angle > 0) {
// angles cannot exceed 180 degree
return false;
}
}
return true;
}
private int getAngle(List<List<Integer>> points, int i) {
// add modular to inspect last two elements
List<Integer> c = points.get(i % points.size());
List<Integer> b = points.get((i + 1) % points.size());
List<Integer> a = points.get((i + 2) % points.size());
return (a.get(1) - b.get(1)) * (b.get(0) - c.get(0)) - (b.get(1) - c.get(1)) * (a.get(0) - b.get(0));
}
}

470. Implement Rand10() Using Rand7()#

https://leetcode.com/problems/implement-rand10-using-rand7/


rand7()是1~7,所以rand7() - 1是0~6,用(rand7() - 1) * 7 + rand7() - 1凑出0~48,丢弃40~48便能得到0~39,对这个结果%10 + 1就是0~10。

一般情况下,如果要用randN()实现randM()M > N,则应该先生成一个中间的X = N^(b + 1),且randX() = N^b * (randN() - 1) + N^(b - 1) * (randN() - 1) + N^(b - 2) * (randN() - 1) + ... + N^0 * (randN() - 1)。有了randX()之后再舍弃一部分值使得randX()的范围是randM()的倍数,这样就能按比例缩小范围生成randM()

/**
* The rand7() API is already defined in the parent class SolBase.
* public int rand7();
* @return a random integer in the range 1 to 7
*/
class Solution extends SolBase {
public int rand10() {
int rand40 = 40;
while (rand40 >= 40) {
rand40 = (rand7() - 1) * 7 + rand7() - 1;
}
return rand40 % 10 + 1;
}
}

471. Encode String with Shortest Length#

https://leetcode.com/problems/encode-string-with-shortest-length/


使用HashMap存储每个子字符串的encode结果,加快中间过程推导。循环遍历每个可能子字符串长度,并尝试对于每个子字符串,是子字符串本身更长,还是压缩编码后更长,不断累加起来。

class Solution {
private Map<String, String> map = new HashMap<>();
public String encode(String s) {
if (map.containsKey(s)) {
return map.get(s);
}
String result = s;
for (int l = 1; l <= result.length(); l++) {
int count = countRepeat(s, l);
// minimize temp string's length
String temp = s.substring(0, l);
if (count > 1) {
temp = encode(temp);
}
int tempLength = temp.length();
// determine whether encoding process does not make the string shorter
for (int r = 1; r <= count; r++) {
StringBuilder builder = new StringBuilder();
// digit + [ + ], or raw temp string
if (tempLength + 3 < tempLength * r) {
builder.append(r).append("[").append(temp).append("]");
} else {
builder.append(s, 0, r * l);
}
builder.append(encode(s.substring(r * l)));
if (builder.length() < result.length()) {
result = builder.toString();
}
}
}
map.put(s, result);
return result;
}
private int countRepeat(String input, int max) {
int i = 1;
while ((i + 1) * max <= input.length()) {
for (int j = 0; j < max; j++) {
if (input.charAt(j) != input.charAt(max * i + j)) {
return i;
}
}
i++;
}
return i;
}
}

472. Concatenated Words#

https://leetcode.com/problems/concatenated-words/


使用Trie,先把所有words按长短排序,把短词优先插入Trie中。之后的长词进来可以尝试进行匹配,长词匹配完到一个短词之后,检查是否之后的部分还能匹配上其他短词,直到整个词都匹配完毕。

class Solution {
public List<String> findAllConcatenatedWordsInADict(String[] words) {
Arrays.sort(words, Comparator.comparingInt(String::length));
Trie trie = new Trie();
List<String> result = new ArrayList<String>();
for (String word : words) {
if (word.length() == 0) {
continue;
}
if (trie.search(word)) {
result.add(word);
} else {
trie.insert(word);
}
}
return result;
}
}
class Trie {
private static class Node {
Node[] children;
boolean isEnd;
Node() {
children = new Node[26];
isEnd = false;
}
}
private Node root = new Node();
public void insert(String key) {
Node curr = root;
for (int i = 0; i < key.length(); i++) {
int index = key.charAt(i) - 'a';
if (curr.children[index] == null) {
curr.children[index] = new Node();
}
curr = curr.children[index];
}
curr.isEnd = true;
}
public boolean search(String key) {
return search(key, 0);
}
private boolean search(String key, int pos) {
if (pos == key.length()) {
return true;
}
Node curr = root;
for (int i = pos; i < key.length(); i++) {
int index = key.charAt(i) - 'a';
if (curr.children[index] == null) {
return false;
}
curr = curr.children[index];
if (curr.isEnd && search(key, i + 1)) {
return true;
}
}
return false;
}
}

473. Matchsticks to Square#

https://leetcode.com/problems/matchsticks-to-square/


使用回溯算法,首先可以把所有火柴的长度加起来,只有总长是4的倍数才能组成正方形,并且如果能组成正方形其边长一定是总和的1/4。有了这个目标就能回溯进行尝试,把所有火柴按长度排序之后,从最长边开始尝试把四边都填满,如果已经当前边已有长度 + 当前火柴太长,或者是发现已有的两条边一样长,就跳过尝试填下一条边。

class Solution {
public boolean makesquare(int[] nums) {
if (nums == null || nums.length == 0) {
return false;
}
int sum = 0;
for (int num : nums) {
sum += num;
}
// must be 4's multiples to form a square
if (sum % 4 != 0) {
return false;
}
Arrays.sort(nums);
// start from the longest matchstick
return backtrack(nums, new int[4], nums.length - 1, sum / 4);
}
boolean backtrack(int[] nums, int[] sums, int index, int target) {
if (index == -1) {
return true;
}
for (int i = 0; i < 4; i++) {
// current sum + edge is too big, or there are already two same edges
if ((sums[i] + nums[index] > target) || (i > 0 && sums[i] == sums[i-1])) {
continue;
}
// try to fill current sums[i]
sums[i] += nums[index];
if (backtrack(nums, sums, index - 1, target)) {
return true;
}
sums[i] -= nums[index];
}
return false;
}
}

474. Ones and Zeroes#

https://leetcode.com/problems/ones-and-zeroes/


动态规划,dp[i][j]表示能用i个0,j个1构成的最大子集大小。遍历每个字符串,每个字符串必然有zero个0和one个1。如果用了当前这个字符串,那么就剩下i - zero个0,j - one个字符串,最大子集就变成了dp[i - zero][j - one] + 1,此处的1指的是当前这个字符串本身也在最大子集内;而如果不用当前这个字符串,最大子集仍然是由dp[i][j]推出。二者的最大值就是目标解。

class Solution {
public int findMaxForm(String[] strs, int m, int n) {
int[][] dp = new int[m + 1][n + 1];
for (String str : strs) {
int one = 0;
int zero = 0;
for (char c : str.toCharArray()) {
if (c == '1') {
one++;
} else {
zero++;
}
}
for (int i = m; i >= zero; i--) {
for (int j = n; j >= one; j--) {
if (zero <= i && one <= j) {
// dp[i][j] is the max number of str we can pick with i "0"s and j "1"s
dp[i][j] = Math.max(dp[i][j], dp[i - zero][j - one] + 1);
}
}
}
}
return dp[m][n];
}
}

475. Heaters#

https://leetcode.com/problems/heaters/


先把househeater排序,之后双指针法,对于每个house找到第一个比它大的heater,有三种情况:

  • heater是全部heaters里最小的,那么这台heater必须要覆盖到当前house,这段距离要被更新到result
  • heater是全部heaters里最大的,那么这台heater也必须要覆盖到当前house,这段距离要被更新到result
  • heater位于house[i-1]house[i]之间,那么比较heater和这两个房子之间的距离,短的那段就是最小需覆盖距离
class Solution {
public int findRadius(int[] houses, int[] heaters) {
Arrays.sort(houses);
Arrays.sort(heaters);
int result = 0;
for (int i = 0, j = 0; i < houses.length; i++) {
while (j < heaters.length && heaters[j] <= houses[i]) {
j++;
}
if (j == 0) {
// the leftmost house must be covered by the first heaters
result = Math.max(result, heaters[0] - houses[i]);
} else if (j == heaters.length) {
// the rightmost house must be covered by the last heaters
result = Math.max(result, houses[i] - heaters[j-1]);
} else {
// heaters[j-1] < houses[i] < heaters[j], choose the min distance between them
result = Math.max(result, Math.min(houses[i] - heaters[j-1], heaters[j] - houses[i]));
}
}
return result;
}
}

476. Number Complement#

https://leetcode.com/problems/number-complement/


按照补码公式推导。

class Solution {
public int findComplement(int num) {
int n = 0;
while (n < num) {
n = 2*n + 1;
}
return n - num;
}
}

477. Total Hamming Distance#

https://leetcode.com/problems/total-hamming-distance/


对于n个数,因为每个数都是32位,遍历每一位,如果n个数中有k个数这一位是1,n-k个数这一位是0,那么这一位就会让总的hamming distance增加k * (n - k)

class Solution {
public int totalHammingDistance(int[] nums) {
int result = 0, n = nums.length;
for (int j = 0; j < 32; j++) {
int bitCount = 0;
// if there are n integers, k of them have a particular bit set and (n-k) do not,
// then that bit contributes k*(n-k) hamming distance to the total
for (int i = 0; i < n; i++) {
bitCount += (nums[i] >> j) & 1;
}
result += bitCount*(n - bitCount);
}
return result;
}
}

478. Generate Random Point in a Circle#

https://leetcode.com/problems/generate-random-point-in-a-circle/


用极坐标形式去理解,因为圆的半径是radius,与x轴形成夹角最大是2 * pi,所以这二者分别乘以一个[0, 1]之间的随机数,就能得到一个圆内的点。

class Solution {
double radius, x_center, y_center;
public Solution(double radius, double x_center, double y_center) {
this.radius = radius;
this.x_center = x_center;
this.y_center = y_center;
}
public double[] randPoint() {
double len = Math.sqrt(Math.random()) * radius;
double deg = Math.random() * 2*Math.PI;
double x = x_center + len * Math.cos(deg);
double y = y_center + len * Math.sin(deg);
return new double[]{x, y};
}
}
/**
* Your Solution object will be instantiated and called as such:
* Solution obj = new Solution(radius, x_center, y_center);
* double[] param_1 = obj.randPoint();
*/

479. Largest Palindrome Product#

https://leetcode.com/problems/largest-palindrome-product/


对于每个n,确定上下界,从大到小穷举回文数pal,再检查sqrt(pal)与上界之间的数是否存在可以被pal整除的i

class Solution {
public int largestPalindrome(int n) {
if (n == 1) {
return 9;
}
long upper = (long)(Math.pow(10, n) - 1);
long lower = upper/10 + 1;
long half = upper * upper / (long)Math.pow(10, n);
while (half > 0) {
// For each palindrome number pal, it checks if the number can be divided by a number i with n digits
long pal = Long.parseLong(half + new StringBuilder().append(half--).reverse().toString());
// As it's in descending order, the first number that meets this check is the largest palindrome number we want
for (long i = upper; i * i >= pal && i > lower; i--) {
if (pal % i == 0) {
return (int) (pal % 1337);
}
}
half--;
}
return 0;
}
}

480. Sliding Window Median#

https://leetcode.com/problems/sliding-window-median/


使用两个优先队列,一个small用于降序存窗口中较小一半的数,一个large用于升序存窗口中较大一半的数,这样可以保证无论窗口长度是奇数还是偶数,中位数都可以通过优先队列的头元素快速求出。之后向前移动窗口时,需要把即将进入窗口的数和当前small中的最大值进行比较,进而判断这个数应该加到small还是large中。因为新加入的数可能会导致两个优先队列长度失衡,因此维护一个balance变量用于动态平衡两个队列的长度。

class Solution {
public double[] medianSlidingWindow(int[] nums, int k) {
if (k > nums.length || k == 0) {
return new double[0];
}
// priority queue will store [array index, array element]
Queue<int[]> small = new PriorityQueue<>((n1, n2) -> n1[1] == n2[1] ? 0 : n2[1] > n1[1] ? 1 : -1); // descending
Queue<int[]> large = new PriorityQueue<>((n1, n2) -> n1[1] == n2[1] ? 0 : n1[1] > n2[1] ? 1 : -1); // ascending
double[] result = new double[nums.length - k + 1];
for (int i = 0; i < k; i++) {
large.offer(new int[]{i, nums[i]});
}
for (int i = 0; i < (k + 1) / 2; i++) {
small.offer(large.poll());
}
int balance = 0;
for (int i = 0; i < result.length; i++) {
// get current median
result[i] = small.peek()[1];
if (k % 2 == 0) {
result[i] = (result[i] + large.peek()[1]) / 2;
}
if (i == result.length - 1) {
break;
}
// decide the incoming number should go to small or large
balance += nums[i] <= small.peek()[1] ? -1 : 1;
if (!small.isEmpty() && nums[i + k] < small.peek()[1]) {
small.add(new int[]{i + k, nums[i + k]});
balance++;
} else {
large.add(new int[]{i + k, nums[i + k]});
balance--;
}
// how many numbers should one queue offers to another queue
if (balance > 0) {
large.offer(small.poll());
balance -= 2;
}
if (balance < 0) {
small.offer(large.poll());
balance += 2;
}
// move forward the sliding window
while (!small.isEmpty() && small.peek()[0] <= i) {
small.poll();
}
while (!large.isEmpty() && large.peek()[0] <= i) {
large.poll();
}
}
return result;
}
}

481. Magical String#

https://leetcode.com/problems/magical-string/


双指针 + 数组,具体使用参考注释。

class Solution {
public int magicalString(int n) {
if (n <= 0) {
return 0;
}
if (n <= 3) {
return 1;
}
// +1 to avoid overflow because the last round head might points to a number 2
int[] a = new int[n + 1];
a[0] = 1;
a[1] = 2;
a[2] = 2;
// head points to the number which will be used to generate new numbers
// tail points to the next empty position to put the new number
int head = 2, tail = 3, num = 1, result = 1;
while (tail < n) {
for (int i = 0; i < a[head]; i++) {
a[tail] = num;
if (num == 1 && tail < n) {
result++;
}
tail++;
}
// flip number back and forth between 1 and 2
num = num ^ 3;
head++;
}
return result;
}
}

482. License Key Formatting#

https://leetcode.com/problems/license-key-formatting/


把原始字符串中的'-'去掉,之后计算长度,把mod K的余数部分放在第一部分,其他每个部分都是K的长度,中间用'-'间隔。

class Solution {
public String licenseKeyFormatting(String S, int K) {
String s = S.replace("-", "");
int len = s.length();
if (S.length() == 0 || len == 0) {
return "";
}
StringBuilder sb = new StringBuilder();
// the first group could be shorter than K
int firstLen = len % K;
if (firstLen >= 1) {
for (int i = 0; i < firstLen; i++) {
char ch = s.charAt(i);
sb.append(Character.toUpperCase(ch));
}
sb.append("-");
}
for (int i = firstLen; i < len; i = i + K) {
// after K characters, add a hyphen
for (int j = 0; j < K; j++) {
char ch = s.charAt(i + j);
sb.append(Character.toUpperCase(ch));
}
sb.append("-");
}
// remove last hyphen
sb.setLength(sb.length() - 1);
return sb.toString();
}
}

483. Smallest Good Base#

https://leetcode.com/problems/smallest-good-base/


算出最大的可能次幂,然后向下循环缩小范围。

class Solution {
public String smallestGoodBase(String n) {
long num = Long.parseLong(n);
// smallest base is 2 so our m must be between 2 and log2n
int maxPower = (int)(Math.log((double)num) / Math.log(2));
for (int i = maxPower; i >= 2; i--) {
// ⌊i-th root of num⌋ is the only candidate that needs to be tested
int root = (int)Math.pow(num, 1.0 / i);
long sum = 1;
long term = 1;
for (int j = 1; j <= i; j++) {
term *= root;
sum += term;
}
// (i^(j+1) - 1) / (i - 1) = num
if (sum == num) {
return Integer.toString(root);
}
}
// else result is num - 1
return Long.toString(num - 1);
}
}

484. Find Permutation#

https://leetcode.com/problems/find-permutation/


先初始化一个有序的序列,之后把所有含'D'的序列区间进行颠倒,颠倒时注意是头尾双指针两两配对交换,交换完后移动指针。

class Solution {
public int[] findPermutation(String s) {
if (s == null || s.isEmpty()) {
return new int[0];
}
int n = s.length();
int[] sorted = new int[n + 1];
for (int i = 0; i <= n; i++) {
sorted[i] = i + 1;
}
int right = 0;
while (right < n) {
if (s.charAt(right) == 'I') {
right++;
continue;
}
// find first 'D' and set as left
int left = right;
while (right < n && s.charAt(right) == 'D') {
right++;
}
// swap intervals with all 'D's
reverse(sorted, left, right);
}
return sorted;
}
// swap all pairs of number in [left, right]
private void reverse(int[] arr, int left, int right) {
while (left < right) {
int tmp = arr[left];
arr[left] = arr[right];
arr[right] = tmp;
left++;
right--;
}
}
}

485. Max Consecutive Ones#

https://leetcode.com/problems/max-consecutive-ones/


遇到1就计数增加,反之检查是否需要更新最大值。注意循环结束后仍然要检查一次。

class Solution {
public int findMaxConsecutiveOnes(int[] nums) {
int max = 0, count = 0;
for (int i: nums) {
if (i == 1) {
count++;
} else {
max = Math.max(max, count);
count = 0;
}
}
return Math.max(max, count);
}
}

486. Predict the Winner#

https://leetcode.com/problems/predict-the-winner/


使用动态规划,dp[i][j]表示如果第一个玩家先手,能够从nums[i, j]之间比第二个玩家多拿多少数字。如果先手玩家拿了nums[i],剩下的nums[i+1, j]里面就会生成dp[i+1][j],则第一个玩家比第二个玩家多的部分就应该是nums[i] + dp[i+1][j]

然而,注意到一旦第一个玩家拿了nums[i]nums[i+1, j]中就变成了第二个玩家先手,因此此时我们看到的dp[i+1][j]实际上是第二个玩家在nums[i+1, j]中比第一个玩家多拿了多少数字(因为两个人都是用最优策略在玩游戏,因此如果把两个人视为一个整体,取数字的顺序一定是固定的,只是谁先手的差异)。

因此,如果先手玩家拿nums[i]dp[i][j]nums[i] - dp[i+1][j],即抵消了子区间内对方多拿的数字。同理,如果先手玩家拿nums[j]dp[i][j]nums[j] - dp[i][j-1],这两种取法选择较大的一种作为最终策略。

class Solution {
public boolean PredictTheWinner(int[] nums) {
int n = nums.length;
// dp[i][j]: how much more scores that the first player will get from i to j than the second player
int[][] dp = new int[n][n];
for (int i = 0; i < n; i++) {
dp[i][i] = nums[i];
}
for (int len = 1; len < n; len++) {
for (int i = 0; i < n - len; i++) {
int j = i + len;
// first player takes num[i], and second player starts first to result dp[i + 1, j]
// or similarily, first player takes num[j], and second player starts first to result dp[i, j - 1]
dp[i][j] = Math.max(nums[i] - dp[i + 1][j], nums[j] - dp[i][j - 1]);
}
}
return dp[0][n - 1] >= 0;
}
}

487. Max Consecutive Ones II#

https://leetcode.com/problems/max-consecutive-ones-ii/


使用两个变量zeroLeftzeroRightzeroRight表示当前遍历时连续遇到的1的个数,遇到0就清零;而zeroLeft则在遇到0时才更新,把此前zeroRight赋值给zeroLeft,这个值包含了此前连续的1的个数 + 1(当前的0可以翻转成1)。最大值由zeroLeft + zeroRight得到。

class Solution {
public int findMaxConsecutiveOnes(int[] nums) {
int result = 0, zeroLeft = 0, zeroRight = 0;
for (int i = 0; i < nums.length; i++) {
zeroRight++;
if (nums[i] == 0) {
zeroLeft = zeroRight;
zeroRight = 0;
}
result = Math.max(result, zeroLeft + zeroRight);
}
return result;
}
}

对于follow-up,需要使用一个queue存储所有0的下标,然后用双指针lowhigh计算最大的连续1区间长度。只要还在flip的上限范围内,就只需要更新high,不需要更新low;而一旦超出上限,就必须把最早的0的下标弹出,更新low

public int findMaxConsecutiveOnes(int[] nums) {
// flip at most k zero
int result = 0, k = 1;
Queue<Integer> zeroIndex = new LinkedList<>();
for (int low = 0, high = 0; high < nums.length; high++) {
if (nums[high] == 0) {
zeroIndex.offer(high);
}
// eject the earliest 0's index
if (zeroIndex.size() > k) {
low = zeroIndex.poll() + 1;
}
result = Math.max(result, high - low + 1);
}
return result;
}

488. Zuma Game#

https://leetcode.com/problems/zuma-game/


分类讨论,单球+2/双球+1/双球之间插1异色球的情况。

class Solution {
private int result = Integer.MAX_VALUE;
private int[] map = new int[26];
private char[] colors = {'R', 'Y', 'B', 'G', 'W'};
public int findMinStep(String board, String hand) {
for (int i = 0; i < hand.length(); i++) {
map[hand.charAt(i) - 'A']++;
}
dfs(new StringBuilder(board), 0);
return result == Integer.MAX_VALUE ? -1 : result;
}
private void dfs(StringBuilder board, int step) {
// skip rest of the dfs if step is already too big, or update the min step
if (step >= result) {
return;
}
if (board.length() == 0) {
result = Math.min(step, result);
return;
}
for (int i = 0; i < board.length(); i++) {
char c = board.charAt(i);
int j = i;
while (j + 1 < board.length() && board.charAt(j + 1) == c) {
j++;
}
if (j == i && map[c - 'A'] >= 2) {
// single ball, insert two same color balls
StringBuilder tmp = new StringBuilder(board);
tmp.insert(i, c + "" + c);
map[c - 'A'] -= 2;
dfs(eliminate(tmp), step + 2);
map[c - 'A'] += 2;
} else if (j == i + 1) {
// two adjacent balls with same color
// insert a same color ball
if (map[c - 'A'] >= 1) {
StringBuilder tmp = new StringBuilder(board);
tmp.insert(i, c);
map[c - 'A']--;
dfs(eliminate(tmp), step + 1);
map[c - 'A']++;
}
// insert a different color ball between these two adjacent balls
for (char color : colors) {
if (color == c) {
continue;
}
if (map[color - 'A'] >= 1) {
StringBuilder tmp = new StringBuilder(board);
tmp.insert(i + 1, color);
map[color - 'A']--;
dfs(eliminate(tmp), step + 1);
map[color - 'A']++;
}
}
}
}
}
private StringBuilder eliminate(StringBuilder sb) {
boolean flag = true;
while (flag) {
flag = false;
for (int i = 0; i < sb.length(); i++) {
int j = i + 1;
while (j < sb.length() && sb.charAt(j) == sb.charAt(i)) {
j++;
}
// remove consecutive balls
if (j - i >= 3) {
sb.delete(i, j);
flag = true;
}
}
}
return sb;
}
}

489. Robot Room Cleaner#

https://leetcode.com/problems/robot-room-cleaner/


回溯算法 + BFS,然后使用一个HashSet存储所有已经走过的方格。

/**
* // This is the robot's control interface.
* // You should not implement it, or speculate about its implementation
* interface Robot {
* // Returns true if the cell in front is open and robot moves into the cell.
* // Returns false if the cell in front is blocked and robot stays in the current cell.
* public boolean move();
*
* // Robot will stay in the same cell after calling turnLeft/turnRight.
* // Each turn will be 90 degrees.
* public void turnLeft();
* public void turnRight();
*
* // Clean the current cell.
* public void clean();
* }
*/
class Solution {
private static int[][] DIR = { {-1, 0}, {0, 1}, {1, 0}, {0, -1} }; // UP, RIGHT, DOWN, LEFT
Set<Pair<Integer, Integer>> visited = new HashSet();
Robot robot;
public void cleanRoom(Robot robot) {
this.robot = robot;
backtrack(0, 0, 0);
}
// dir: 0: up, 1: right, 2: down, 3: left
public void backtrack(int x, int y, int dir) {
visited.add(new Pair(x, y));
robot.clean();
for (int i = 0; i < 4; i++) {
int newDir = (dir + i) % 4;
int[] move = DIR[newDir];
int newX = x + move[0];
int newY = y + move[1];
if (!visited.contains(new Pair(newX, newY)) && robot.move()) {
backtrack(newX, newY, newDir);
// go back
robot.turnRight();
robot.turnRight();
robot.move();
robot.turnRight();
robot.turnRight();
}
// turn the robot following chosen direction : clockwise
robot.turnRight();
}
}
}

490. The Maze#

https://leetcode.com/problems/the-maze/


使用DFS + BFS,先用BFS尝试一个方向,然后在这个方向上一条路走到黑直到撞墙(即判断是否还在迷宫范围内),之后再使用DFS从新的位置开始下一次尝试,直到小球抵达终点。

class Solution {
private static final int[][] DIRS = { {1,0},{-1,0},{0,1},{0,-1} };
public boolean hasPath(int[][] maze, int[] start, int[] destination) {
boolean[][] visited = new boolean[maze.length][maze[0].length];
return dfs(maze, start, destination, visited);
}
private boolean dfs(int[][] maze, int[] start, int[] end, boolean[][] visited) {
// start and end points are the same
if (start[0] == end[0] && start[1] == end[1]) {
return true;
}
// already visited grid
if (visited[start[0]][start[1]]) {
return false;
}
visited[start[0]][start[1]] = true;
for (int[] dir : DIRS) {
int x = dir[0] + start[0];
int y = dir[1] + start[1];
// ball always runs until it hit a wall
while (isValid(maze, x, y) && maze[x][y] == 0) {
x += dir[0];
y += dir[1];
}
x -= dir[0];
y -= dir[1];
// current grid is unvisited, try subsequent dfs
if (!visited[x][y]) {
if (dfs(maze, new int[]{x, y}, end, visited)) {
return true;
}
}
}
return false;
}
// still in maze's range
private boolean isValid(int[][] maze, int x, int y) {
return x >= 0 && y >= 0 && x < maze.length && y < maze[0].length;
}
}

491. Increasing Subsequences#

https://leetcode.com/problems/increasing-subsequences/


回溯算法,分类讨论。如果当前数字比此前的数字更大,可以把它添加到当前list中,然后dfs尝试之后的数组区间,直到访问到数组尾。而另一种情况下,即便当前数字比此前数字更大,我们也不一定用这个数字,而是向后继续搜索找到另一个递增数字。所以综合起来,共有2种触发dfs的方式。

class Solution {
public List<List<Integer>> findSubsequences(int[] nums) {
List<List<Integer>> result = new ArrayList<>();
dfs(result, nums, 0, Integer.MIN_VALUE, new ArrayList<>());
return result;
}
private void dfs(List<List<Integer>> result, int[] nums, int index, int pre, List<Integer> cur) {
// reach the end of nums, add current list to result
if (index == nums.length) {
if (cur.size() > 1) {
result.add(new ArrayList(cur));
}
return;
}
// current number is larger than previous number, add to current list
if (nums[index] >= pre) {
cur.add(nums[index]);
dfs(result, nums, index + 1, nums[index], cur);
cur.remove(cur.size() - 1);
}
// may or may not use current number, so try next number
if (nums[index] != pre) {
dfs(result, nums, index + 1, pre, cur);
}
}
}

492. Construct the Rectangle#

https://leetcode.com/problems/construct-the-rectangle/


通过开方可以快速定位到离sqrt(area)最近的质因数。

class Solution {
public int[] constructRectangle(int area) {
int width = (int)Math.sqrt(area);
while (area % width != 0) {
width--;
}
return new int[]{area / width, width};
}
}

493. Reverse Pairs#

https://leetcode.com/problems/reverse-pairs/


归并排序,在归并的同时搜索reverse pairs。

class Solution {
public int reversePairs(int[] nums) {
return nums == null ? 0 : mergeSort(nums, 0, nums.length - 1);
}
public int mergeSort(int[] nums, int start, int end) {
if (start >= end) {
return 0;
}
int mid = start + ((end - start) >> 1);
int result = mergeSort(nums, start, mid) + mergeSort(nums, mid + 1, end);
int[] cache = new int[end - start + 1];
int k = mid + 1, c = 0;
for (int i = start, j = mid + 1; i <= mid; i++, c++) {
// find pairs
while (j <= end && nums[i] > 2 * (long)nums[j]) {
j++;
}
// find numbers smaller than nums[i] and copy to cache
while (k <= end && nums[k] < nums[i]) {
cache[c] = nums[k];
c++;
k++;
}
cache[c] = nums[i];
// update result
result += j - (mid + 1);
}
while (k <= end) {
cache[c] = nums[k];
c++;
k++;
}
System.arraycopy(cache, 0, nums, start, end - start + 1);
return result;
}
}

494. Target Sum#

https://leetcode.com/problems/target-sum/


nums可以拆成两部分来看,一部分用于添加加号,另一部分用于添加负号。如果把这两部分分别用posnegative两个集合表示,很显然有sum(pos) - sum(negative) = s,化简得到2*sum(pos) = s + sum。根据这个推论,我们可以把问题转化为典型01背包问题,即从nums中找出一个子集(组成pos),使得子集里所有元素的和为(s + sum) / 2

寻找子集可以使用动态规划,其中dp[i][j]表示使用前i个数得到目标数j共有几种方式。我们遍历nums中的每个数i,对0到target之间的状态j进行穷举,并分类讨论:

  • 当前的目标j太小,第i个数太大,肯定没有办法选择它,因此dp[i][j]的值仍然与dp[i-1][j]相同
  • 否则,第i个数可选或者可不选。如果不选,与此前的情况一样;如果选了,就相当于之前的i-1个数字总和必须为j - 第i个数的值

最后,二维dp可以进一步优化成一维dp,详见注释。

class Solution {
public int findTargetSumWays(int[] nums, int s) {
int sum = 0;
for (int num : nums) {
sum += num;
}
// too big, too small, or mismatched parity
if (Math.abs(s) > sum || (s+sum) % 2 != 0) {
return 0;
}
// sum(pos) - sum(negative) = s
// 2 * sum(pos) = s + sum(negative) + sum(pos) = s + sum
return subsetSum(nums, (s+sum) / 2);
}
public int subsetSum(int[] nums, int target) {
// int[] dp = new int[target + 1];
// dp[0] = 1;
// for (int num : nums) {
// for (int i = target; i >= num; i--) {
// dp[i] += dp[i - num];
// }
// }
// return dp[target];
int[][] dp = new int[nums.length + 1][target + 1];
dp[0][0] = 1;
for (int i = 1; i <= nums.length; i++) {
for (int j = 0; j <= target; j++) {
if (j < nums[i - 1]) {
// current j is too small, so only possible to repeating all the ways to get sum j by using first i-1 elements
dp[i][j] = dp[i - 1][j];
} else {
// otherwise, could also add nums[i] on top of each way to get sum j-nums[i-1] using first i elements
dp[i][j] = dp[i - 1][j] + dp[i - 1][j - nums[i - 1]];
}
}
}
return dp[nums.length][target];
}
}

495. Teemo Attacking#

https://leetcode.com/problems/teemo-attacking/


除了最后一个数,前面所有数两两之间的间隔都要和duration进行比较,取较小值累加;最后一个数直接累加duration即可。

class Solution {
public int findPoisonedDuration(int[] timeSeries, int duration) {
if (timeSeries == null || timeSeries.length == 0) {
return 0;
}
int result = 0;
for (int i = 0; i < timeSeries.length - 1; i++) {
if (timeSeries[i + 1] - timeSeries[i] > duration) {
result += duration;
} else {
result += timeSeries[i + 1] - timeSeries[i];
}
}
result += duration;
return result;
}
}

496. Next Greater Element I#

https://leetcode.com/problems/next-greater-element-i/


使用一个HashMap,先储存nums2中所有数字的下标,之后遍历nums1,获取到每个数在nums2中的下标,然后向后搜索看能不能找到比他大的数。

class Solution {
public int[] nextGreaterElement(int[] nums1, int[] nums2) {
// <number, number index>
Map<Integer, Integer> indexMap = new HashMap<>();
for (int i = 0; i < nums2.length; i++) {
indexMap.put(nums2[i], i);
}
int[] result = new int[nums1.length];
for (int i = 0; i < result.length; i++) {
result[i] = getNext(nums2, indexMap.get(nums1[i]));
}
return result;
}
// start from current number's index in nums2, and search afterwards
private int getNext(int[] nums, int index) {
int curr = nums[index];
for (int i = index + 1; i < nums.length; i++) {
if (nums[i] > curr) {
return nums[i];
}
}
return -1;
}
}

497. Random Point in Non-overlapping Rectangles#

https://leetcode.com/problems/random-point-in-non-overlapping-rectangles/


计算每个矩形的面积,注意因为边界上的点也需要用到,因此边长需要+1。得到每个矩形的面积后用数组存储,然后以总的面积和为上限,得到一个范围,从这个范围内生成一个随机数,利用二分查找找到这个随机数对应的是哪个矩形(即,找到数组内的最小下标left,使得这个随机数落在arr[left - 1]arr[left]的区间之内)。找到矩形后,再提取这个矩形的坐标,生成范围内的随机坐标点。

class Solution {
private Random rand;
private int[][] re;
private int N;
private int sum;
private int[] arr;
public Solution(int[][] rects) {
rand = new Random();
re = rects;
N = rects.length;
arr = new int[N];
for (int i = 0; i < N; i++) {
arr[i] = area(rects[i]);
}
// each i in the arr represents a range of (arr[i-1], arr[i]]
sum = arr[0];
for (int i = 1; i < N; i++) {
sum += arr[i];
arr[i] = arr[i-1] + arr[i];
}
}
// boundaries are used, so + 1
public int area(int[] rect) {
return (rect[2] - rect[0] + 1) * (rect[3] - rect[1] + 1);
}
public int[] pick() {
int num = rand.nextInt(sum) + 1;
// find the index l, which is at the left most idx that makes arr[l] >= num (arr[l-1] < num <= arr[l])
int left = 0;
int right = arr.length - 1;
while (left < right) {
int mid = left + (right - left) / 2;
if (arr[mid] >= num) {
right = mid;
} else {
left = mid + 1;
}
}
int x1 = re[left][0];
int y1 = re[left][1];
int x2 = re[left][2];
int y2 = re[left][3];
int x = rand.nextInt(x2 - x1 + 1) + x1;
int y = rand.nextInt(y2 - y1 + 1) + y1;
return new int[]{x, y};
}
}
/**
* Your Solution object will be instantiated and called as such:
* Solution obj = new Solution(rects);
* int[] param_1 = obj.pick();
*/

498. Diagonal Traverse#

https://leetcode.com/problems/diagonal-traverse/


观察奇偶性,发现向上移动时,行与列下标相加总是偶数;向下移动时,总是奇数。运用此规律分类讨论,并注意单独分析边界掉头转向的情况。

class Solution {
public int[] findDiagonalOrder(int[][] matrix) {
if (matrix.length == 0) {
return new int[0];
}
int r = 0, c = 0, m = matrix.length, n = matrix[0].length, result[] = new int[m * n];
for (int i = 0; i < result.length; i++) {
result[i] = matrix[r][c];
if ((r + c) % 2 == 0) {
if (c == n - 1) {
// move to next row, go downward
r++;
} else if (r == 0) {
// move to next column, go downward
c++;
} else {
r--;
c++;
}
} else {
if (r == m - 1) {
// move to next column, go upward
c++;
} else if (c == 0) {
// move to next row, go upward
r++;
} else {
r++;
c--;
}
}
}
return result;
}
}

499. The Maze III#

https://leetcode.com/problems/the-maze-iii/


从起始点开始,遍历4个可能的方向,对任何一个方向,尝试在这个方向上一条路走到黑,如果撞墙了就尝试左右转向。不断递归尝试,看最后是否有情况下能到达hole。维护两个变量minSmin,分别表示已走过的最短成功路径字符串和最短成功路径的长度,再对应维护两个变量表示当前路径的详情和长度,每走一步就更新当前变量,如果到达hole看是否能更新最小值。

class Solution {
private int min; // min distance to hole
private String minS; // min distance's path string
private int[] hole;
private int[][] maze;
private int[][] map; // shortest distant traveling from ball to this point
private int[][] dirs = { {0, 1}, {-1, 0}, {1, 0}, {0, -1} }; // right, up, down, left
public String findShortestWay(int[][] maze, int[] ball, int[] hole) {
this.min = Integer.MAX_VALUE;
this.minS = null;
this.hole = hole;
this.maze = maze;
this.map = new int[maze.length][maze[0].length];
for (int i = 0; i < map.length; i++) {
Arrays.fill(map[i], Integer.MAX_VALUE);
}
move(ball[0], ball[1], 0, "", -1);
return minS == null ? "impossible" : minS;
}
private void move(int r, int c, int cnt, String path, int dir) {
// current path's length is not shortest
if (cnt > min || cnt > map[r][c]) {
return;
}
// exclude start point
if (dir != -1) {
// add current diretions to path
if (dir == 0) {
path += 'r';
} else if (dir == 1) {
path += 'u';
} else if (dir == 2) {
path += 'd';
} else {
path += 'l';
}
while (r >= 0 && r < maze.length && c >= 0 && c < maze[0].length && maze[r][c] == 0) {
map[r][c] = Math.min(map[r][c], cnt);
// reach the hole
if (r == hole[0] && c == hole[1]) {
// lexicographically smallest way
if (cnt == min && path.compareTo(minS) < 0) {
minS = path;
} else if (cnt < min) {
min = cnt;
minS = path;
}
return;
}
// roll along direction
r += dirs[dir][0];
c += dirs[dir][1];
cnt++;
}
// [r, c] is wall, need to walk back 1 step
r -= dirs[dir][0];
c -= dirs[dir][1];
cnt--;
}
// hit wall (or start) -> try to turn
for (int i = 0; i < dirs.length; i++) {
// don't keep going or go back
if (dir == i || dir == 3 - i) {
continue;
}
int newR = r + dirs[i][0];
int newC = c + dirs[i][1];
// new space is still valid
if (newR >= 0 && newR < maze.length && newC >= 0 && newC < maze[0].length && maze[newR][newC] == 0) {
move(r, c, cnt, path, i);
}
}
}
}

500. Keyboard Row#

https://leetcode.com/problems/keyboard-row/


统计每个单词中的字母分别在每行出现了几次,如果只在一行出现,就把它添加到结果中。

class Solution {
public String[] findWords(String[] words) {
String[] strs = {"qwertyuiop","asdfghjkl","zxcvbnm"};
List<String> results = new ArrayList();
for (String word : words) {
int count1 = 0, count2 = 0, count3 = 0;
for (char c : word.toCharArray()) {
if (strs[0].indexOf(c) != -1) {
count1++;
}
if (strs[1].indexOf(c) != -1) {
count2++;
}
if (strs[2].indexOf(c) != -1) {
count3++;
}
}
// only comes from one row
if ((count1 == 0 && count2 == 0) || (count1 == 0 && count3 == 0) || (count2 == 0 && count3 == 0)) {
results.add(word);
}
}
return results.toArray(new String[results.size()]);
}
}
Profile Image of the Author
MikeYan01
Hello world!
站点统计
文章
23
分类
7
标签
12
总字数
215,225
运行时长
0
最后活动
0 天前
1
401. Binary Watch
2
402. Remove K Digits
3
403. Frog Jump
4
404. Sum of Left Leaves
5
405. Convert a Number to Hexadecimal
6
406. Queue Reconstruction by Height
7
407. Trapping Rain Water II
8
408. Valid Word Abbreviation
9
409. Longest Palindrome
10
410. Split Array Largest Sum
11
411. Minimum Unique Word Abbreviation
12
412. Fizz Buzz
13
413. Arithmetic Slices
14
414. Third Maximum Number
15
415. Add Strings
16
416. Partition Equal Subset Sum
17
417. Pacific Atlantic Water Flow
18
418. Sentence Screen Fitting
19
419. Battleships in a Board
20
420. Strong Password Checker
21
421. Maximum XOR of Two Numbers in an Array
22
422. Valid Word Square
23
423. Reconstruct Original Digits from English
24
424. Longest Repeating Character Replacement
25
425. Word Squares
26
426. Convert Binary Search Tree to Sorted Doubly Linked List
27
427. Construct Quad Tree
28
428. Serialize and Deserialize N-ary Tree
29
429. N-ary Tree Level Order Traversal
30
430. Flatten a Multilevel Doubly Linked List
31
431. Encode N-ary Tree to Binary Tree
32
432. All O`one Data Structure
33
433. Minimum Genetic Mutation
34
434. Number of Segments in a String
35
435. Non-overlapping Intervals
36
436. Find Right Interval
37
437. Path Sum III
38
438. Find All Anagrams in a String
39
439. Ternary Expression Parser
40
440. K-th Smallest in Lexicographical Order
41
441. Arranging Coins
42
442. Find All Duplicates in an Array
43
443. String Compression
44
444. Sequence Reconstruction
45
445. Add Two Numbers II
46
446. Arithmetic Slices II - Subsequence
47
447. Number of Boomerangs
48
448. Find All Numbers Disappeared in an Array
49
449. Serialize and Deserialize BST
50
450. Delete Node in a BST
51
451. Sort Characters By Frequency
52
452. Minimum Number of Arrows to Burst Balloons
53
453. Minimum Moves to Equal Array Elements
54
454. 4Sum II
55
455. Assign Cookies
56
456. 132 Pattern
57
457. Circular Array Loop
58
458. Poor Pigs
59
459. Repeated Substring Pattern
60
460. LFU Cache
61
461. Hamming Distance
62
462. Minimum Moves to Equal Array Elements II
63
463. Island Perimeter
64
464. Can I Win
65
465. Optimal Account Balancing
66
466. Count The Repetitions
67
467. Unique Substrings in Wraparound String
68
468. Validate IP Address
69
469. Convex Polygon
70
470. Implement Rand10() Using Rand7()
71
471. Encode String with Shortest Length
72
472. Concatenated Words
73
473. Matchsticks to Square
74
474. Ones and Zeroes
75
475. Heaters
76
476. Number Complement
77
477. Total Hamming Distance
78
478. Generate Random Point in a Circle
79
479. Largest Palindrome Product
80
480. Sliding Window Median
81
481. Magical String
82
482. License Key Formatting
83
483. Smallest Good Base
84
484. Find Permutation
85
485. Max Consecutive Ones
86
486. Predict the Winner
87
487. Max Consecutive Ones II
88
488. Zuma Game
89
489. Robot Room Cleaner
90
490. The Maze
91
491. Increasing Subsequences
92
492. Construct the Rectangle
93
493. Reverse Pairs
94
494. Target Sum
95
495. Teemo Attacking
96
496. Next Greater Element I
97
497. Random Point in Non-overlapping Rectangles
98
498. Diagonal Traverse
99
499. The Maze III
100
500. Keyboard Row
文章目录
1
401. Binary Watch
2
402. Remove K Digits
3
403. Frog Jump
4
404. Sum of Left Leaves
5
405. Convert a Number to Hexadecimal
6
406. Queue Reconstruction by Height
7
407. Trapping Rain Water II
8
408. Valid Word Abbreviation
9
409. Longest Palindrome
10
410. Split Array Largest Sum
11
411. Minimum Unique Word Abbreviation
12
412. Fizz Buzz
13
413. Arithmetic Slices
14
414. Third Maximum Number
15
415. Add Strings
16
416. Partition Equal Subset Sum
17
417. Pacific Atlantic Water Flow
18
418. Sentence Screen Fitting
19
419. Battleships in a Board
20
420. Strong Password Checker
21
421. Maximum XOR of Two Numbers in an Array
22
422. Valid Word Square
23
423. Reconstruct Original Digits from English
24
424. Longest Repeating Character Replacement
25
425. Word Squares
26
426. Convert Binary Search Tree to Sorted Doubly Linked List
27
427. Construct Quad Tree
28
428. Serialize and Deserialize N-ary Tree
29
429. N-ary Tree Level Order Traversal
30
430. Flatten a Multilevel Doubly Linked List
31
431. Encode N-ary Tree to Binary Tree
32
432. All O`one Data Structure
33
433. Minimum Genetic Mutation
34
434. Number of Segments in a String
35
435. Non-overlapping Intervals
36
436. Find Right Interval
37
437. Path Sum III
38
438. Find All Anagrams in a String
39
439. Ternary Expression Parser
40
440. K-th Smallest in Lexicographical Order
41
441. Arranging Coins
42
442. Find All Duplicates in an Array
43
443. String Compression
44
444. Sequence Reconstruction
45
445. Add Two Numbers II
46
446. Arithmetic Slices II - Subsequence
47
447. Number of Boomerangs
48
448. Find All Numbers Disappeared in an Array
49
449. Serialize and Deserialize BST
50
450. Delete Node in a BST
51
451. Sort Characters By Frequency
52
452. Minimum Number of Arrows to Burst Balloons
53
453. Minimum Moves to Equal Array Elements
54
454. 4Sum II
55
455. Assign Cookies
56
456. 132 Pattern
57
457. Circular Array Loop
58
458. Poor Pigs
59
459. Repeated Substring Pattern
60
460. LFU Cache
61
461. Hamming Distance
62
462. Minimum Moves to Equal Array Elements II
63
463. Island Perimeter
64
464. Can I Win
65
465. Optimal Account Balancing
66
466. Count The Repetitions
67
467. Unique Substrings in Wraparound String
68
468. Validate IP Address
69
469. Convex Polygon
70
470. Implement Rand10() Using Rand7()
71
471. Encode String with Shortest Length
72
472. Concatenated Words
73
473. Matchsticks to Square
74
474. Ones and Zeroes
75
475. Heaters
76
476. Number Complement
77
477. Total Hamming Distance
78
478. Generate Random Point in a Circle
79
479. Largest Palindrome Product
80
480. Sliding Window Median
81
481. Magical String
82
482. License Key Formatting
83
483. Smallest Good Base
84
484. Find Permutation
85
485. Max Consecutive Ones
86
486. Predict the Winner
87
487. Max Consecutive Ones II
88
488. Zuma Game
89
489. Robot Room Cleaner
90
490. The Maze
91
491. Increasing Subsequences
92
492. Construct the Rectangle
93
493. Reverse Pairs
94
494. Target Sum
95
495. Teemo Attacking
96
496. Next Greater Element I
97
497. Random Point in Non-overlapping Rectangles
98
498. Diagonal Traverse
99
499. The Maze III
100
500. Keyboard Row