LeetCode 476-500. 题解合集
文章目录
- [LeetCode 476-500. 题解合集](#LeetCode 476-500. 题解合集)
-
- [476. Number Complement(数字的补数)](#476. Number Complement(数字的补数))
- [477. Total Hamming Distance(汉明距离总和)](#477. Total Hamming Distance(汉明距离总和))
- [478. Generate Random Point in a Circle(在圆内随机生成点)](#478. Generate Random Point in a Circle(在圆内随机生成点))
- [479. Largest Palindrome Product(最大回文数乘积)](#479. Largest Palindrome Product(最大回文数乘积))
- [480. Sliding Window Median(滑动窗口中位数)](#480. Sliding Window Median(滑动窗口中位数))
- [481. Magical String(神奇字符串)](#481. Magical String(神奇字符串))
- [482. License Key Formatting(密钥格式化)](#482. License Key Formatting(密钥格式化))
- [483. Smallest Good Base(最小好进制)](#483. Smallest Good Base(最小好进制))
- [484. Find Permutation(寻找排列)](#484. Find Permutation(寻找排列))
- [485. Max Consecutive Ones(最大连续 1 的个数)](#485. Max Consecutive Ones(最大连续 1 的个数))
- [486. Predict the Winner(预测赢家)](#486. Predict the Winner(预测赢家))
- [487. Max Consecutive Ones II(最大连续1的个数 II)](#487. Max Consecutive Ones II(最大连续1的个数 II))
- [488. Zuma Game(祖玛游戏)](#488. Zuma Game(祖玛游戏))
- [489. Robot Room Cleaner(扫地机器人)](#489. Robot Room Cleaner(扫地机器人))
- [490. The Maze(迷宫)](#490. The Maze(迷宫))
- [491. Non-decreasing Subsequences(非递减子序列)](#491. Non-decreasing Subsequences(非递减子序列))
- [492. Construct the Rectangle(构造矩形)](#492. Construct the Rectangle(构造矩形))
- [493. Reverse Pairs(翻转对)](#493. Reverse Pairs(翻转对))
- [494. Target Sum(目标和)](#494. Target Sum(目标和))
- [495. Teemo Attacking(提莫攻击)](#495. Teemo Attacking(提莫攻击))
- [496. Next Greater Element I(下一个更大元素 I)](#496. Next Greater Element I(下一个更大元素 I))
- [497. Random Point in Non-overlapping Rectangles(非重叠矩形中的随机点)](#497. Random Point in Non-overlapping Rectangles(非重叠矩形中的随机点))
- [498. Diagonal Traverse(对角线遍历)](#498. Diagonal Traverse(对角线遍历))
- [499. The Maze III(迷宫 III)](#499. The Maze III(迷宫 III))
- [500. Keyboard Row(键盘行)](#500. Keyboard Row(键盘行))
476. Number Complement(数字的补数)
题目描述
对整数的二进制表示取反(0 变 1,1 变 0)后转换为十进制,得到该整数的补数。
给定整数 num,输出它的补数。补数只翻转 num 的二进制表示中有效位(不含前导零)。
示例
输入 `num = 5`,输出 `2`(5 的二进制 `101` → 取反 `010` = 2)
输入 `num = 1`,输出 `0`
提示:
1 <= num < 2^31
解题思路
解法一(位掩码异或):找到 num 最高有效位的位置,构造一个与该位宽相同、全部为 1 的掩码 mask,
则补数为 num ^ mask。例如 num = 5(二进制 101),mask = 111(7),5 ^ 7 = 2。
代码实现
java
import java.util.*;
class Solution {
public int findComplement(int num) {
int mask = 0;
for (int i = num; i > 0; i >>= 1) {
mask = (mask << 1) | 1;
}
return num ^ mask;
}
public static void main(String[] args) {
Solution sol = new Solution();
int pass = 0, total = 0;
++total;
if (sol.findComplement(5) == 2) { ++pass; } else { System.out.println("FAIL test1"); }
++total;
if (sol.findComplement(1) == 0) { ++pass; } else { System.out.println("FAIL test2"); }
++total;
if (sol.findComplement(0) == 0) { ++pass; } else { System.out.println("FAIL test3"); }
if (pass == total) {
System.out.println("== 0476 PASS ==");
} else {
System.out.println("== 0476 FAIL ==");
System.exit(1);
}
}
}
复杂度分析
| 解法 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 解法一 | O(log num) | O(1) |
477. Total Hamming Distance(汉明距离总和)
题目描述
两个整数的汉明距离指它们二进制对应位不同的数量。给定整数数组 nums,
返回 nums 中任意两个数之间汉明距离的总和。
示例
输入 `nums = [4,14,2]`,输出 `6`(HammingDistance(4,14)+HammingDistance(4,2)+HammingDistance(14,2)=2+2+2=6)
输入 `nums = [4,14,4]`,输出 `4`
提示:
1 <= nums.length <= 10^4,0 <= nums[i] <= 10^9
解题思路
逐位统计:对于第 b 位,设有 c 个数该位为 1,则 n-c 个数该位为 0,这一位对总距离的贡献为
c * (n-c)(每一对 1/0 在该位产生 1 的距离)。对所有 32 位求和即可,无需两两比较。
代码实现
java
import java.util.*;
class Solution {
public int totalHammingDistance(int[] nums) {
int total = 0;
for (int b = 0; b < 32; ++b) {
int cnt = 0;
for (int x : nums) {
if (((x >> b) & 1) == 1) ++cnt;
}
total += cnt * (nums.length - cnt);
}
return total;
}
public static void main(String[] args) {
Solution sol = new Solution();
int pass = 0, total = 0;
++total;
if (sol.totalHammingDistance(new int[]{4, 14, 2}) == 6) { ++pass; } else { System.out.println("FAIL test1"); }
++total;
if (sol.totalHammingDistance(new int[]{4, 14, 4}) == 4) { ++pass; } else { System.out.println("FAIL test2"); }
++total;
if (sol.totalHammingDistance(new int[]{3, 10, 5, 25, 2, 8}) == 37) { ++pass; } else { System.out.println("FAIL test3"); }
if (pass == total) {
System.out.println("== 0477 PASS ==");
} else {
System.out.println("== 0477 FAIL ==");
System.exit(1);
}
}
}
复杂度分析
| 解法 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 逐位统计 | O(n * 32) | O(1) |
478. Generate Random Point in a Circle(在圆内随机生成点)
题目描述
给定圆的半径 radius 和圆心 (x_center, y_center),实现 Solution 类:
构造器用 radius、x_center、y_center 初始化对象;randPoint() 在闭圆内(圆周上的点视为圆内)
均匀随机返回一点,以数组 [x, y] 表示。
示例
输入 `["Solution","randPoint","randPoint","randPoint"]`,`[[1.0,0.0,0.0],[],[],[]]`
输出 `[null,[-0.02493,-0.38077],[0.82314,0.38945],[0.36572,0.17248]]`
提示:
0 < radius <= 10^8,-10^7 <= x_center,y_center <= 10^7,randPoint最多调用3*10^4次。
解题思路
解法一(拒绝采样):在圆心为 (x_center, y_center)、边长为 2*radius 的外接正方形内均匀取点,
若该点到圆心距离 <= radius 则接受,否则重试。几何上能保证圆内均匀分布,期望重试次数常数。
代码实现
java
import java.util.*;
class Solution {
private double r;
private double xc;
private double yc;
private Random rand;
public Solution(double radius, double x_center, double y_center) {
r = radius;
xc = x_center;
yc = y_center;
rand = new Random();
}
public double[] randPoint() {
while (true) {
double x = xc + (rand.nextDouble() * 2 - 1) * r;
double y = yc + (rand.nextDouble() * 2 - 1) * r;
double dx = x - xc;
double dy = y - yc;
if (dx * dx + dy * dy <= r * r) {
return new double[]{x, y};
}
}
}
public static void main(String[] args) {
Solution sol = new Solution(1.0, 0.0, 0.0);
int pass = 0, total = 0;
int ok = 0;
for (int i = 0; i < 5000; i++) {
double[] p = sol.randPoint();
double dx = p[0] - 0.0;
double dy = p[1] - 0.0;
if (dx * dx + dy * dy <= 1.0 + 1e-9) ++ok;
}
++total;
if (ok == 5000) { ++pass; } else { System.out.println("FAIL test1: ok=" + ok); }
Solution sol2 = new Solution(5.0, 2.0, -3.0);
int ok2 = 0;
for (int i = 0; i < 5000; i++) {
double[] p = sol2.randPoint();
double dx = p[0] - 2.0;
double dy = p[1] + 3.0;
if (dx * dx + dy * dy <= 25.0 + 1e-9) ++ok2;
}
++total;
if (ok2 == 5000) { ++pass; } else { System.out.println("FAIL test2: ok2=" + ok2); }
if (pass == total) {
System.out.println("== 0478 PASS ==");
} else {
System.out.println("== 0478 FAIL ==");
System.exit(1);
}
}
}
复杂度分析
| 解法 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 解法一 | 期望 O(1)(拒绝采样) | O(1) |
479. Largest Palindrome Product(最大回文数乘积)
题目描述
给定整数 n,返回可表示为两个 n 位整数乘积的最大回文整数 。答案可能很大,返回其对 1337 取余的结果。
示例
输入 `n = 2`,输出 `987`(99 x 91 = 9009,9009 % 1337 = 987)
输入 `n = 1`,输出 `9`
提示:
1 <= n <= 8
解题思路
解法一(构造回文 + 试除):枚举回文数的左半部分 left(max 递减到 max/10,max = 10^n - 1),
由 left 对称构造出回文数 p(如 left = 9 构造 99,left = 91 构造 9119)。
再判断是否存在 i ∈ [sqrt(p), max] 使得 p % i == 0 且 p / i <= max,存在即返回 p % 1337。
代码实现
java
import java.util.*;
class Solution {
public int largestPalindrome(int n) {
if (n == 1) return 9;
int max = (int) Math.pow(10, n) - 1;
for (int left = max; left > max / 10; --left) {
long p = createPalindrome(left);
for (long i = max; i * i >= p; --i) {
if (p % i == 0 && p / i <= max) {
return (int) (p % 1337);
}
}
}
return -1;
}
private long createPalindrome(long left) {
long res = left;
while (left > 0) {
res = res * 10 + left % 10;
left /= 10;
}
return res;
}
public static void main(String[] args) {
Solution sol = new Solution();
int pass = 0, total = 0;
++total;
if (sol.largestPalindrome(2) == 987) { ++pass; } else { System.out.println("FAIL test1: " + sol.largestPalindrome(2)); }
++total;
if (sol.largestPalindrome(1) == 9) { ++pass; } else { System.out.println("FAIL test2"); }
++total;
if (sol.largestPalindrome(3) == 123) { ++pass; } else { System.out.println("FAIL test3: " + sol.largestPalindrome(3)); }
if (pass == total) {
System.out.println("== 0479 PASS ==");
} else {
System.out.println("== 0479 FAIL ==");
System.exit(1);
}
}
}
复杂度分析
| 解法 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 解法一 | O(10^n * 10^(n/2)) | O(1) |
480. Sliding Window Median(滑动窗口中位数)
题目描述
给定数组 nums 与窗口大小 k,窗口从左滑到右,每次移动 1 位。返回每次窗口内元素的中位数组成的数组。
若窗口长度为偶数,中位数为中间两数的平均值。与真实值误差在 10^-5 内视为正确。
示例
`nums = [1,3,-1,-3,5,3,6,7]`,`k = 3` → `[1,-1,-1,3,5,6]`
提示:
k始终有效(k<=nums.length)。
解题思路
解法一(双堆 + 延迟删除):用最大堆 lo 存较小半、最小堆 hi 存较大半,维持 lo.size() 等于或比 hi 多 1。
滑出窗口的元素不直接删除,而是记入 delayed 哈希表,移动指针时再惰性剔除堆顶过期元素,保证各操作 O(log k)。
解法二(有序列表 + 二分插入):用 ArrayList 维护窗口的有序序列,Collections.binarySearch 定位插入/删除位置,
中位数直接按下标取。实现简单,但单次维护为 O(k)。
代码实现
java
import java.util.*;
class Solution {
// 解法一:双堆 + 延迟删除
public double[] medianSlidingWindow(int[] nums, int k) {
int n = nums.length;
double[] res = new double[n - k + 1];
PriorityQueue<Integer> lo = new PriorityQueue<>(Collections.reverseOrder());
PriorityQueue<Integer> hi = new PriorityQueue<>();
Map<Integer, Integer> delayed = new HashMap<>();
int loSize = 0, hiSize = 0;
for (int i = 0; i < n; ++i) {
int x = nums[i];
if (lo.isEmpty() || x <= lo.peek()) { lo.add(x); ++loSize; }
else { hi.add(x); ++hiSize; }
if (i >= k) {
int y = nums[i - k];
delayed.put(y, delayed.getOrDefault(y, 0) + 1);
if (y <= lo.peek()) --loSize; else --hiSize;
}
while (!lo.isEmpty() && delayed.getOrDefault(lo.peek(), 0) > 0) {
delayed.put(lo.peek(), delayed.get(lo.peek()) - 1);
lo.poll();
}
while (!hi.isEmpty() && delayed.getOrDefault(hi.peek(), 0) > 0) {
delayed.put(hi.peek(), delayed.get(hi.peek()) - 1);
hi.poll();
}
while (loSize > hiSize + 1) { hi.add(lo.poll()); --loSize; ++hiSize; }
while (hiSize > loSize) { lo.add(hi.poll()); --hiSize; ++loSize; }
if (i >= k - 1) {
if (loSize > hiSize) res[i - k + 1] = lo.peek();
else res[i - k + 1] = ((double) lo.peek() + hi.peek()) / 2.0;
}
}
return res;
}
// 解法二:有序列表 + 二分插入
public double[] medianSlidingWindow2(int[] nums, int k) {
int n = nums.length;
double[] res = new double[n - k + 1];
List<Integer> win = new ArrayList<>();
for (int i = 0; i < n; ++i) {
int pos = Collections.binarySearch(win, nums[i]);
if (pos < 0) pos = -pos - 1;
win.add(pos, nums[i]);
if (i >= k) {
int rpos = Collections.binarySearch(win, nums[i - k]);
win.remove(rpos);
}
if (i >= k - 1) {
if (k % 2 == 1) res[i - k + 1] = win.get(k / 2);
else res[i - k + 1] = (win.get(k / 2 - 1) + win.get(k / 2)) / 2.0;
}
}
return res;
}
public static void main(String[] args) {
Solution sol = new Solution();
int pass = 0, total = 0;
double[] e1 = sol.medianSlidingWindow(new int[]{1, 3, -1, -3, 5, 3, 6, 7}, 3);
double[] g1 = {1, -1, -1, 3, 5, 6};
++total;
if (e1.length == g1.length) {
boolean same = true;
for (int i = 0; i < g1.length; ++i) if (Math.abs(e1[i] - g1[i]) > 1e-5) same = false;
if (same) ++pass; else System.out.println("FAIL test1: " + Arrays.toString(e1));
} else System.out.println("FAIL test1 len");
double[] e2 = sol.medianSlidingWindow2(new int[]{1, 3, -1, -3, 5, 3, 6, 7}, 3);
++total;
if (e2.length == g1.length) {
boolean same = true;
for (int i = 0; i < g1.length; ++i) if (Math.abs(e2[i] - g1[i]) > 1e-5) same = false;
if (same) ++pass; else System.out.println("FAIL test2: " + Arrays.toString(e2));
} else System.out.println("FAIL test2 len");
if (pass == total) {
System.out.println("== 0480 PASS ==");
} else {
System.out.println("== 0480 FAIL ==");
System.exit(1);
}
}
}
复杂度分析
| 解法 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 解法一 | O(n log k) | O(k) |
| 解法二 | O(n k) | O(k) |
481. Magical String(神奇字符串)
题目描述
神奇字符串 s 仅由 1 和 2 组成,且把 s 中连续的 1 段、2 段的长度依次连起来,正好得到 s 自身。
s 以 "1221121221221121122......" 开头。给定整数 n,返回 s 前 n 个数字中 1 的个数。
示例
输入 `n = 6`,输出 `3`(前 6 个字符 `"122112"`,含 3 个 `1`)
输入 `n = 1`,输出 `1`
提示:
1 <= n <= 10^5
解题思路
解法一(递推生成):从 s = [1,2,2] 出发,用指针 i 读取 s[i] 得知接下来要连续追加几个数字,追加的数字在 1 与 2 之间交替。
追加时统计 1 的个数即可。由于只需前 n 位,生成到长度 n 即停。
代码实现
java
import java.util.*;
class Solution {
public int magicalString(int n) {
if (n <= 0) return 0;
if (n <= 3) return 1;
int[] s = new int[n + 1];
s[0] = 1; s[1] = 2; s[2] = 2;
int res = 1; // 前 3 位里 1 的个数
int i = 2; // 当前读取位置
int next = 1; // 下一个要追加的数字(1 与 2 交替)
int pos = 3;
while (pos < n) {
int cnt = s[i]; // 要连续追加 cnt 个 next
for (int k = 0; k < cnt && pos < n; ++k) {
s[pos++] = next;
if (next == 1) ++res;
}
next = 3 - next; // 1 <-> 2 切换
++i;
}
return res;
}
public static void main(String[] args) {
Solution sol = new Solution();
int pass = 0, total = 0;
++total;
if (sol.magicalString(6) == 3) { ++pass; } else { System.out.println("FAIL test1: " + sol.magicalString(6)); }
++total;
if (sol.magicalString(1) == 1) { ++pass; } else { System.out.println("FAIL test2"); }
++total;
if (sol.magicalString(4) == 2) { ++pass; } else { System.out.println("FAIL test3: " + sol.magicalString(4)); }
++total;
if (sol.magicalString(10) == 5) { ++pass; } else { System.out.println("FAIL test4: " + sol.magicalString(10)); }
if (pass == total) {
System.out.println("== 0481 PASS ==");
} else {
System.out.println("== 0481 FAIL ==");
System.exit(1);
}
}
}
复杂度分析
| 解法 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 解法一 | O(n) | O(n) |
482. License Key Formatting(密钥格式化)
题目描述
给定许可密钥字符串 s(仅含字母、数字、破折号)和整数 k。重新格式化 s,使每组含 k 个字符,
除第一组可短于 k 外其余均恰为 k 个;组间插入破折号,并全部转为大写。返回格式化后的密钥。
示例
输入 `s = "5F3Z-2e-9-w"`,`k = 4`,输出 `"5F3Z-2E9W"`
输入 `s = "2-5g-3-J"`,`k = 2`,输出 `"2-5G-3J"`
提示:
1 <= s.length <= 10^5,1 <= k <= 10^4
解题思路
解法一(从右向左):从末尾向右前扫描,跳过原破折号,每凑满 k 个字符插入一个破折号,并转大写,
最后反转得到结果。一次遍历即可,无需额外大结构。
代码实现
java
import java.util.*;
class Solution {
public String licenseKeyFormatting(String s, int k) {
StringBuilder sb = new StringBuilder();
int cnt = 0;
for (int i = s.length() - 1; i >= 0; --i) {
char c = s.charAt(i);
if (c == '-') continue;
if (cnt == k) { sb.append('-'); cnt = 0; }
sb.append(Character.toUpperCase(c));
++cnt;
}
return sb.reverse().toString();
}
public static void main(String[] args) {
Solution sol = new Solution();
int pass = 0, total = 0;
++total;
if ("5F3Z-2E9W".equals(sol.licenseKeyFormatting("5F3Z-2e-9-w", 4))) { ++pass; } else { System.out.println("FAIL test1: " + sol.licenseKeyFormatting("5F3Z-2e-9-w", 4)); }
++total;
if ("2-5G-3J".equals(sol.licenseKeyFormatting("2-5g-3-J", 2))) { ++pass; } else { System.out.println("FAIL test2: " + sol.licenseKeyFormatting("2-5g-3-J", 2)); }
++total;
if ("A-A-A-A-A".equals(sol.licenseKeyFormatting("a-a-a-a-a", 1))) { ++pass; } else { System.out.println("FAIL test3: " + sol.licenseKeyFormatting("a-a-a-a-a", 1)); }
if (pass == total) {
System.out.println("== 0482 PASS ==");
} else {
System.out.println("== 0482 FAIL ==");
System.exit(1);
}
}
}
复杂度分析
| 解法 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 解法一 | O(n) | O(n) |
483. Smallest Good Base(最小好进制)
题目描述
以字符串形式给出 n,返回 n 的最小好进制 k(k >= 2)。若 n 在 k 进制下所有数位全为 1,
则称 k 为 n 的好进制。返回以字符串形式给出的 k。
示例
输入 `n = "13"`,输出 `"3"`(13 的 3 进制为 111)
输入 `n = "4681"`,输出 `"8"`(8 进制为 11111)
输入 `n = "1000000000000000000"`,输出 `"999999999999999999"`
提示:
3 <= n <= 10^18,n无前导零。
解题思路
解法一(按长度二分):若 n 在 k 进制下为 m 位全 1,则 n = 1 + k + k^2 + ... + k^(m-1)。
枚举位数 m(从大到小,最多约 60),对每个 m 在 [2, n^(1/(m-1))] 内二分底数 k,
用等比数列求和(带溢出保护)判断是否能拼出 n。位数越多所需的 k 越小,故先找到的即为最小。
代码实现
java
import java.util.*;
class Solution {
public String smallestGoodBase(String n) {
long num = Long.parseLong(n);
for (int m = 63; m >= 2; --m) {
long lo = 2;
long hi = (long) (Math.pow(num, 1.0 / (m - 1))) + 1;
while (lo <= hi) {
long mid = lo + (hi - lo) / 2;
long sum = 0, p = 1;
boolean overflow = false;
for (int i = 0; i < m; ++i) {
sum += p;
if (sum > num) { overflow = true; break; }
p *= mid;
if (p < 0) { overflow = true; break; }
}
if (overflow) { hi = mid - 1; continue; }
if (sum == num) return Long.toString(mid);
else if (sum < num) lo = mid + 1;
else hi = mid - 1;
}
}
return Long.toString(num - 1);
}
public static void main(String[] args) {
Solution sol = new Solution();
int pass = 0, total = 0;
++total;
if ("3".equals(sol.smallestGoodBase("13"))) { ++pass; } else { System.out.println("FAIL test1: " + sol.smallestGoodBase("13")); }
++total;
if ("8".equals(sol.smallestGoodBase("4681"))) { ++pass; } else { System.out.println("FAIL test2: " + sol.smallestGoodBase("4681")); }
++total;
if ("999999999999999999".equals(sol.smallestGoodBase("1000000000000000000"))) { ++pass; } else { System.out.println("FAIL test3: " + sol.smallestGoodBase("1000000000000000000")); }
if (pass == total) {
System.out.println("== 0483 PASS ==");
} else {
System.out.println("== 0483 FAIL ==");
System.exit(1);
}
}
}
复杂度分析
| 解法 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 解法一 | O(log^2 n) | O(1) |
484. Find Permutation(寻找排列)
题目描述
由 [1, n] 内整数组成的排列 perm 可用长度为 n-1 的字符串 s 表示:若 perm[i] < perm[i+1] 则 s[i] = 'I',否则 s[i] = 'D'。
给定 s,重构字典序最小的排列并返回。
示例
输入 `s = "I"`,输出 `[1, 2]`
输入 `s = "DI"`,输出 `[2, 1, 3]`
提示:
1 <= s.length <= 10^5,s[i]只含'D'或'I'。
解题思路
解法一(整体反转):先将 res 初始化为 [1, 2, ..., n]。扫描 s,每当遇到一段连续的 'D'(从 i 到 j),
就把 res[i..j](含端点,共 j-i+1 个数)整体反转------这恰好使该下降段变为递减且字典序最小。其余 'I' 不动。
代码实现
java
import java.util.*;
class Solution {
public int[] findPermutation(String s) {
int n = s.length() + 1;
int[] res = new int[n];
for (int i = 0; i < n; ++i) res[i] = i + 1;
int i = 0;
while (i < s.length()) {
if (s.charAt(i) == 'D') {
int j = i;
while (j < s.length() && s.charAt(j) == 'D') ++j;
reverse(res, i, j);
i = j;
}
++i;
}
return res;
}
private void reverse(int[] a, int i, int j) {
while (i < j) {
int t = a[i]; a[i] = a[j]; a[j] = t;
++i; --j;
}
}
public static void main(String[] args) {
Solution sol = new Solution();
int pass = 0, total = 0;
++total;
if (Arrays.equals(sol.findPermutation("I"), new int[]{1, 2})) { ++pass; } else { System.out.println("FAIL test1: " + Arrays.toString(sol.findPermutation("I"))); }
++total;
if (Arrays.equals(sol.findPermutation("DI"), new int[]{2, 1, 3})) { ++pass; } else { System.out.println("FAIL test2: " + Arrays.toString(sol.findPermutation("DI"))); }
++total;
if (Arrays.equals(sol.findPermutation("DD"), new int[]{3, 2, 1})) { ++pass; } else { System.out.println("FAIL test3: " + Arrays.toString(sol.findPermutation("DD"))); }
++total;
if (Arrays.equals(sol.findPermutation("IDDI"), new int[]{1, 4, 3, 2, 5})) { ++pass; } else { System.out.println("FAIL test4: " + Arrays.toString(sol.findPermutation("IDDI"))); }
if (pass == total) {
System.out.println("== 0484 PASS ==");
} else {
System.out.println("== 0484 FAIL ==");
System.exit(1);
}
}
}
复杂度分析
| 解法 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 解法一 | O(n) | O(n) |
485. Max Consecutive Ones(最大连续 1 的个数)
题目描述
给定一个二进制数组 nums,计算其中最大连续 1 的个数。
示例
输入 `nums = [1,1,0,1,1,1]`,输出 `3`
输入 `nums = [1,0,1,1,0,1]`,输出 `2`
提示:
1 <= nums.length <= 10^5,nums[i]为0或1。
解题思路
解法一(一次扫描):维护当前连续 1 的长度 cur 与最大值 max。遇 1 则 cur++ 并更新 max,遇 0 则 cur 归零。
代码实现
java
import java.util.*;
class Solution {
public int findMaxConsecutiveOnes(int[] nums) {
int max = 0, cur = 0;
for (int x : nums) {
if (x == 1) {
++cur;
if (cur > max) max = cur;
} else {
cur = 0;
}
}
return max;
}
public static void main(String[] args) {
Solution sol = new Solution();
int pass = 0, total = 0;
++total;
if (sol.findMaxConsecutiveOnes(new int[]{1, 1, 0, 1, 1, 1}) == 3) { ++pass; } else { System.out.println("FAIL test1"); }
++total;
if (sol.findMaxConsecutiveOnes(new int[]{1, 0, 1, 1, 0, 1}) == 2) { ++pass; } else { System.out.println("FAIL test2"); }
++total;
if (sol.findMaxConsecutiveOnes(new int[]{0, 0, 0}) == 0) { ++pass; } else { System.out.println("FAIL test3"); }
++total;
if (sol.findMaxConsecutiveOnes(new int[]{1, 1, 1, 1}) == 4) { ++pass; } else { System.out.println("FAIL test4"); }
if (pass == total) {
System.out.println("== 0485 PASS ==");
} else {
System.out.println("== 0485 FAIL ==");
System.exit(1);
}
}
}
复杂度分析
| 解法 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 解法一 | O(n) | O(1) |
486. Predict the Winner(预测赢家)
题目描述
给定非负整数数组 nums,玩家 1(先手)与玩家 2 轮流从数组任意一端取数并累加得分,取完为止。
两人均按使自己得分最大化的方式游玩。若玩家 1 得分 >= 玩家 2(含相等),返回 true。
示例
输入 `nums = [1,5,2]`,输出 `false`(玩家 1 最多得 3,玩家 2 得 5)
输入 `nums = [1,5,233,7]`,输出 `true`
提示:
1 <= nums.length <= 20,0 <= nums[i] <= 10^7
解题思路
解法一(极小化极大 / 记忆化搜索):定义 dfs(i, j) 为在子区间 [i, j] 上,当前玩家相对对手能多拿的分数。
选左端:nums[i] - dfs(i+1, j);选右端:nums[j] - dfs(i, j-1),取较大者。先手分数为 dfs(0, n-1),非负即胜。
解法二(动态规划):令 dp[i][j] 表示区间 [i, j] 上先手相对后手的差值,转移同解法一,自底向上填表,最终 dp[0][n-1] >= 0。
代码实现
java
import java.util.*;
class Solution {
private int[][] memo;
// 解法一:记忆化搜索
public boolean predictTheWinner(int[] nums) {
int n = nums.length;
memo = new int[n][n];
for (int[] row : memo) Arrays.fill(row, Integer.MIN_VALUE);
return dfs(nums, 0, n - 1) >= 0;
}
private int dfs(int[] nums, int i, int j) {
if (i == j) return nums[i];
if (memo[i][j] != Integer.MIN_VALUE) return memo[i][j];
int pickLeft = nums[i] - dfs(nums, i + 1, j);
int pickRight = nums[j] - dfs(nums, i, j - 1);
return memo[i][j] = Math.max(pickLeft, pickRight);
}
// 解法二:动态规划
public boolean predictTheWinner2(int[] nums) {
int n = nums.length;
int[][] dp = new int[n][n];
for (int i = 0; i < n; ++i) dp[i][i] = nums[i];
for (int len = 2; len <= n; ++len) {
for (int i = 0; i + len - 1 < n; ++i) {
int j = i + len - 1;
int pickLeft = nums[i] - dp[i + 1][j];
int pickRight = nums[j] - dp[i][j - 1];
dp[i][j] = Math.max(pickLeft, pickRight);
}
}
return dp[0][n - 1] >= 0;
}
public static void main(String[] args) {
Solution sol = new Solution();
int pass = 0, total = 0;
++total;
if (sol.predictTheWinner(new int[]{1, 5, 2}) == false) { ++pass; } else { System.out.println("FAIL test1"); }
++total;
if (sol.predictTheWinner(new int[]{1, 5, 233, 7}) == true) { ++pass; } else { System.out.println("FAIL test2"); }
++total;
if (sol.predictTheWinner2(new int[]{1, 5, 2}) == false) { ++pass; } else { System.out.println("FAIL test3"); }
++total;
if (sol.predictTheWinner2(new int[]{1, 5, 233, 7}) == true) { ++pass; } else { System.out.println("FAIL test4"); }
if (pass == total) {
System.out.println("== 0486 PASS ==");
} else {
System.out.println("== 0486 FAIL ==");
System.exit(1);
}
}
}
复杂度分析
| 解法 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 解法一 | O(n^2) | O(n) |
| 解法二 | O(n^2) | O(n^2) |
487. Max Consecutive Ones II(最大连续1的个数 II)
题目描述
给定二进制数组 nums,若最多可以翻转一个 0(将其变为 1),返回数组中连续 1 的最大个数。
示例
输入 `nums = [1,0,1,1,0]`,输出 `4`
输入 `nums = [1,0,1,1,0,1]`,输出 `4`
提示:
1 <= nums.length <= 10^5,nums[i]为0或1。
解题思路
解法一(滑动窗口):用双指针维护窗口,允许窗口内至多 1 个 0。右指针扩张,遇到 0 计数 zero++;
当 zero > 1 时左指针右移(若移出的是 0 则 zero--),使窗口重新合法。窗口最大长度即答案。
代码实现
java
import java.util.*;
class Solution {
public int findMaxConsecutiveOnes(int[] nums) {
int max = 0, zero = 0, left = 0;
for (int right = 0; right < nums.length; ++right) {
if (nums[right] == 0) ++zero;
while (zero > 1) {
if (nums[left] == 0) --zero;
++left;
}
if (right - left + 1 > max) max = right - left + 1;
}
return max;
}
public static void main(String[] args) {
Solution sol = new Solution();
int pass = 0, total = 0;
++total;
if (sol.findMaxConsecutiveOnes(new int[]{1, 0, 1, 1, 0}) == 4) { ++pass; } else { System.out.println("FAIL test1: " + sol.findMaxConsecutiveOnes(new int[]{1, 0, 1, 1, 0})); }
++total;
if (sol.findMaxConsecutiveOnes(new int[]{1, 0, 1, 1, 0, 1}) == 4) { ++pass; } else { System.out.println("FAIL test2: " + sol.findMaxConsecutiveOnes(new int[]{1, 0, 1, 1, 0, 1})); }
++total;
if (sol.findMaxConsecutiveOnes(new int[]{0, 0, 0}) == 1) { ++pass; } else { System.out.println("FAIL test3: " + sol.findMaxConsecutiveOnes(new int[]{0, 0, 0})); }
++total;
if (sol.findMaxConsecutiveOnes(new int[]{1, 1, 1}) == 3) { ++pass; } else { System.out.println("FAIL test4: " + sol.findMaxConsecutiveOnes(new int[]{1, 1, 1})); }
if (pass == total) {
System.out.println("== 0487 PASS ==");
} else {
System.out.println("== 0487 FAIL ==");
System.exit(1);
}
}
}
复杂度分析
| 解法 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 解法一 | O(n) | O(1) |
488. Zuma Game(祖玛游戏)
题目描述
桌上有一排球('R'/'Y'/'B'/'G'/'W'),手中也有若干彩球。每回合从手中选一球插入桌面排球任意位置,
若出现 >=3 个同色相连则移除(连锁移除直至不再满足),目标是清空桌面。给定 board 与 hand,返回清空所需的最少手球数;不能清空返回 -1。
示例
输入 `board = "WRRBBW"`,`hand = "RB"`,输出 `-1`
输入 `board = "WWRRBBWW"`,`hand = "WRBRW"`,输出 `2`
输入 `board = "G"`,`hand = "GGGGG"`,输出 `-1`
提示:
- 所有输入合法,颜色只有 5 种。
解题思路
解法一(BFS + 状态去重):把 (当前桌面串, 剩余手牌) 作为状态,手牌用排序字符串以便去重。
每步枚举插入的球颜色与插入位置(避免在同一同色段重复插入),插入后执行「消除 >=3 同色」的级联收缩,
得到新状态入队。最先到达空桌面的步数即为最少手球数。
代码实现
java
import java.util.*;
class Solution {
private static final String COLORS = "RYBGW";
public int findMinStep(String board, String hand) {
String sortedHand = sortStr(hand);
Queue<State> q = new LinkedList<>();
Set<String> visited = new HashSet<>();
q.offer(new State(board, sortedHand, 0));
visited.add(board + "|" + sortedHand);
while (!q.isEmpty()) {
State st = q.poll();
if (st.board.isEmpty()) return st.steps;
for (int i = 0; i <= st.board.length(); ++i) {
for (int c = 0; c < 5; ++c) {
char ball = COLORS.charAt(c);
if (st.hand.indexOf(ball) < 0) continue;
// 跳过冗余位置:若插入点左侧桌上字符与待插球同色,则在 i 与 i-1 插入等价
if (i > 0 && st.board.charAt(i - 1) == ball) continue;
String newBoard = shrink(insertAt(st.board, i, ball));
String newHand = removeOne(st.hand, ball);
String key = newBoard + "|" + newHand;
if (!visited.contains(key)) {
visited.add(key);
q.offer(new State(newBoard, newHand, st.steps + 1));
}
}
}
}
return -1;
}
private String insertAt(String board, int pos, char ball) {
return board.substring(0, pos) + ball + board.substring(pos);
}
private String shrink(String s) {
while (true) {
boolean changed = false;
int i = 0;
while (i < s.length()) {
int j = i;
while (j < s.length() && s.charAt(j) == s.charAt(i)) ++j;
if (j - i >= 3) {
s = s.substring(0, i) + s.substring(j);
changed = true;
break;
}
i = j;
}
if (!changed) break;
}
return s;
}
private String sortStr(String s) {
char[] a = s.toCharArray();
Arrays.sort(a);
return new String(a);
}
private String removeOne(String sortedHand, char ball) {
int idx = sortedHand.indexOf(ball);
if (idx < 0) return sortedHand;
return sortedHand.substring(0, idx) + sortedHand.substring(idx + 1);
}
static class State {
String board;
String hand;
int steps;
State(String b, String h, int s) { board = b; hand = h; steps = s; }
}
public static void main(String[] args) {
Solution sol = new Solution();
int pass = 0, total = 0;
++total;
if (sol.findMinStep("WRRBBW", "RB") == -1) { ++pass; } else { System.out.println("FAIL test1: " + sol.findMinStep("WRRBBW", "RB")); }
++total;
if (sol.findMinStep("WWRRBBWW", "WRBRW") == 2) { ++pass; } else { System.out.println("FAIL test2: " + sol.findMinStep("WWRRBBWW", "WRBRW")); }
++total;
if (sol.findMinStep("G", "GGGGG") == 2) { ++pass; } else { System.out.println("FAIL test3: " + sol.findMinStep("G", "GGGGG")); }
++total;
if (sol.findMinStep("RRWWRRBBRR", "WB") == 2) { ++pass; } else { System.out.println("FAIL test4: " + sol.findMinStep("RRWWRRBBRR", "WB")); }
if (pass == total) {
System.out.println("== 0488 PASS ==");
} else {
System.out.println("== 0488 FAIL ==");
System.exit(1);
}
}
}
复杂度分析
| 解法 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 解法一 | O(状态数 * 串长) | O(状态数) |
489. Robot Room Cleaner(扫地机器人)
题目描述
房间建模为 m x n 的二进制网格(1 为空地,0 为障碍物)。机器人从未知空地出发,只能使用其 API(move/turnLeft/turnRight/clean)
控制它清扫整个房间(清理每个空单元格)。初始方向向上,且无法访问网格本身。需"盲解"------在不知房间布局下仅靠 API 完成任务。
示例
输入 `room = [[1,1,1,1,1,0,1,1],[1,1,1,1,1,0,1,1],[1,0,1,1,1,1,1,1],[0,0,0,1,0,0,0,0],[1,1,1,1,1,1,1,1]]`,`row = 1`,`col = 3`
输出 Robot cleaned all rooms(清理所有 1 单元格)
提示:
1 <= m <= 100,1 <= n <= 200。
解题思路
解法一(DFS + 回溯):把机器人当前朝向维护为 dir(上/右/下/左),用 (x, y) 哈希集合记录已访问单元。
每到一个新单元就 clean() 并标记;之后朝四个方向依次尝试:若 move() 成功则递归进入、回溯时掉头退回原位并掉头复位,
否则原地右转。这样不依赖全局网格即可遍历全部可达空地。
代码实现
java
import java.util.*;
interface Robot {
boolean move();
void turnLeft();
void turnRight();
void clean();
}
class Solution {
private static final int[] DX = {-1, 0, 1, 0}; // 上、右、下、左
private static final int[] DY = {0, 1, 0, -1};
public void cleanRoom(Robot robot) {
Set<String> visited = new HashSet<>();
dfs(0, 0, 0, robot, visited);
}
private void dfs(int x, int y, int dir, Robot robot, Set<String> visited) {
String key = x + "," + y;
if (visited.contains(key)) return;
visited.add(key);
robot.clean();
for (int step = 0; step < 4; ++step) {
if (robot.move()) {
int nx = x + DX[dir];
int ny = y + DY[dir];
dfs(nx, ny, dir, robot, visited);
// 回溯:掉头回到原单元并复位朝向
robot.turnRight();
robot.turnRight();
robot.move();
robot.turnRight();
robot.turnRight();
}
robot.turnRight();
dir = (dir + 1) % 4;
}
}
public static void main(String[] args) {
// 在 5x8 的房间上做功能验证:机器人应清理所有 1 单元格
int[][] room = {
{1, 1, 1, 1, 1, 0, 1, 1},
{1, 1, 1, 1, 1, 0, 1, 1},
{1, 0, 1, 1, 1, 1, 1, 1},
{0, 0, 0, 1, 0, 0, 0, 0},
{1, 1, 1, 1, 1, 1, 1, 1}
};
int total1 = 0;
for (int[] row : room) for (int v : row) if (v == 1) ++total1;
TestRobot robot = new TestRobot(room, 1, 3);
new Solution().cleanRoom(robot);
int pass = 0, total = 0;
++total;
if (robot.cleaned == total1) { ++pass; } else { System.out.println("FAIL test1: cleaned=" + robot.cleaned + " total=" + total1); }
if (pass == total) {
System.out.println("== 0489 PASS ==");
} else {
System.out.println("== 0489 FAIL ==");
System.exit(1);
}
}
static class TestRobot implements Robot {
int[][] room;
int x, y, dir; // dir: 0=上 1=右 2=下 3=左
int cleaned = 0;
TestRobot(int[][] room, int sx, int sy) {
this.room = room; this.x = sx; this.y = sy; this.dir = 0;
}
public boolean move() {
int nx = x + DX[dir];
int ny = y + DY[dir];
if (nx >= 0 && nx < room.length && ny >= 0 && ny < room[0].length && room[nx][ny] == 1) {
x = nx; y = ny;
return true;
}
return false;
}
public void turnLeft() { dir = (dir + 3) % 4; }
public void turnRight() { dir = (dir + 1) % 4; }
public void clean() { if (room[x][y] == 1) ++cleaned; }
}
}
复杂度分析
| 解法 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 解法一 | O(可达空地数) | O(可达空地数) |
490. The Maze(迷宫)
题目描述
由空地(0)与墙(1)组成的迷宫 maze 中有一个球。球可向上/下/左/右滚动,遇墙前不会停止;停下后才可选下一方向。
给定 maze、起点 start 与终点 destination,判断球能否在 destination 停下(true/false)。可假定迷宫边缘皆为墙。
示例
`maze = [[0,0,1,0,0],[0,0,0,0,0],[0,0,0,1,0],[1,1,0,1,1],[0,0,0,0,0]]`,`start = [0,4]`,`destination = [4,4]` → `true`
同上 `destination = [3,2]` → `false`
提示:
1 <= m, n <= 100。
解题思路
解法一(BFS):从 start 出发,对每个方向一直滚到撞墙前的停点(停点是该方向能到达的"真实位置")。若停点未访问则入队,
遇 destination 即返回 true。由于球只在停点决策,状态只需记录停点坐标。
代码实现
java
import java.util.*;
class Solution {
public boolean hasPath(int[][] maze, int[] start, int[] destination) {
int m = maze.length, n = maze[0].length;
boolean[][] visited = new boolean[m][n];
int[][] dirs = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
Queue<int[]> q = new LinkedList<>();
q.offer(start);
visited[start[0]][start[1]] = true;
while (!q.isEmpty()) {
int[] cur = q.poll();
if (cur[0] == destination[0] && cur[1] == destination[1]) return true;
for (int[] d : dirs) {
int x = cur[0], y = cur[1];
while (x + d[0] >= 0 && x + d[0] < m && y + d[1] >= 0 && y + d[1] < n
&& maze[x + d[0]][y + d[1]] == 0) {
x += d[0]; y += d[1];
}
if (!visited[x][y]) {
visited[x][y] = true;
q.offer(new int[]{x, y});
}
}
}
return false;
}
public static void main(String[] args) {
Solution sol = new Solution();
int pass = 0, total = 0;
int[][] maze = {
{0, 0, 1, 0, 0},
{0, 0, 0, 0, 0},
{0, 0, 0, 1, 0},
{1, 1, 0, 1, 1},
{0, 0, 0, 0, 0}
};
++total;
if (sol.hasPath(maze, new int[]{0, 4}, new int[]{4, 4}) == true) { ++pass; } else { System.out.println("FAIL test1"); }
++total;
if (sol.hasPath(maze, new int[]{0, 4}, new int[]{3, 2}) == false) { ++pass; } else { System.out.println("FAIL test2"); }
++total;
if (sol.hasPath(maze, new int[]{0, 4}, new int[]{0, 4}) == true) { ++pass; } else { System.out.println("FAIL test3"); }
if (pass == total) {
System.out.println("== 0490 PASS ==");
} else {
System.out.println("== 0490 FAIL ==");
System.exit(1);
}
}
}
复杂度分析
| 解法 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 解法一 | O(m n) | O(m n) |
491. Non-decreasing Subsequences(非递减子序列)
题目描述
给定整数数组 nums,找出并返回所有不同的、长度至少为 2 的非递减子序列(可任意顺序返回)。数组可能含重复元素,相等也视作非递减。
示例
输入 `nums = [4,6,7,7]`,输出 `[[4,6],[4,6,7],[4,6,7,7],[4,7],[4,7,7],[6,7],[6,7,7],[7,7]]`
输入 `nums = [4,4,3,2,1]`,输出 `[[4,4]]`
提示:
1 <= nums.length <= 15,-100 <= nums[i] <= 100。
解题思路
解法一(回溯 + 层内去重):DFS 枚举每个位置"选/不选"。为得到不同子序列,在每一层递归里用 Set 记录本层已选过的数值,
避免在同一层选相同数字导致重复结果;只有当前数字 >= 序列末尾(或序列为空)时才可选,保证非递减。
代码实现
java
import java.util.*;
class Solution {
public List<List<Integer>> findSubsequences(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
dfs(nums, 0, new ArrayList<>(), res);
return res;
}
private void dfs(int[] nums, int u, List<Integer> t, List<List<Integer>> res) {
if (t.size() >= 2) res.add(new ArrayList<>(t));
Set<Integer> used = new HashSet<>();
for (int i = u; i < nums.length; ++i) {
if (used.contains(nums[i])) continue;
if (t.isEmpty() || nums[i] >= t.get(t.size() - 1)) {
used.add(nums[i]);
t.add(nums[i]);
dfs(nums, i + 1, t, res);
t.remove(t.size() - 1);
}
}
}
public static void main(String[] args) {
Solution sol = new Solution();
int pass = 0, total = 0;
++total;
List<List<Integer>> r1 = sol.findSubsequences(new int[]{4, 6, 7, 7});
if (r1.size() == 8) { ++pass; } else { System.out.println("FAIL test1: size=" + r1.size()); }
++total;
List<List<Integer>> r2 = sol.findSubsequences(new int[]{4, 4, 3, 2, 1});
if (r2.size() == 1 && r2.get(0).equals(Arrays.asList(4, 4))) { ++pass; } else { System.out.println("FAIL test2: " + r2); }
if (pass == total) {
System.out.println("== 0491 PASS ==");
} else {
System.out.println("== 0491 FAIL ==");
System.exit(1);
}
}
}
复杂度分析
| 解法 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 解法一 | O(2^n) | O(n) |
492. Construct the Rectangle(构造矩形)
题目描述
给定矩形页面面积 area,返回 [L, W]:要求面积等于 area、L >= W、且 L 与 W 的差尽可能小。
示例
输入 `area = 4`,输出 `[2, 2]`
输入 `area = 37`,输出 `[37, 1]`
输入 `area = 122122`,输出 `[427, 286]`
提示:
1 <= area <= 10^7
解题思路
解法一(从平方根向下取整):从 w = floor(sqrt(area)) 开始向下找第一个能整除 area 的 w,此时 L = area / w 且 L - W 最小(越接近平方根差越小)。
代码实现
java
import java.util.*;
class Solution {
public int[] constructRectangle(int area) {
int w = (int) Math.sqrt(area);
while (area % w != 0) --w;
return new int[]{area / w, w};
}
public static void main(String[] args) {
Solution sol = new Solution();
int pass = 0, total = 0;
++total;
if (Arrays.equals(sol.constructRectangle(4), new int[]{2, 2})) { ++pass; } else { System.out.println("FAIL test1: " + Arrays.toString(sol.constructRectangle(4))); }
++total;
if (Arrays.equals(sol.constructRectangle(37), new int[]{37, 1})) { ++pass; } else { System.out.println("FAIL test2: " + Arrays.toString(sol.constructRectangle(37))); }
++total;
if (Arrays.equals(sol.constructRectangle(122122), new int[]{427, 286})) { ++pass; } else { System.out.println("FAIL test3: " + Arrays.toString(sol.constructRectangle(122122))); }
if (pass == total) {
System.out.println("== 0492 PASS ==");
} else {
System.out.println("== 0492 FAIL ==");
System.exit(1);
}
}
}
复杂度分析
| 解法 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 解法一 | O(sqrt(area)) | O(1) |
493. Reverse Pairs(翻转对)
题目描述
给定数组 nums,若 i < j 且 nums[i] > 2 * nums[j],则称 (i, j) 为一个重要翻转对。返回重要翻转对的数量。
示例
输入 `[1,3,2,3,1]`,输出 `2`
输入 `[2,4,3,5,1]`,输出 `3`
提示:
- 数组长度不超过
50000,元素均在 32 位整数范围。
解题思路
解法一(归并排序):在合并两个有序子区间时,先统计跨区间的翻转对------对左半每个 i,在右半中找满足 nums[i] > 2*nums[j] 的 j 的个数(右半有序,双指针线性计数),再做标准合并。整体 O(n log n)。
解法二(树状数组 + 离散化):把 nums[i] 与 2*nums[i] 的值域离散化,从左向右扫描:对当前 x,用树状数组查询已插入且 > 2x 的元素个数,再把 x 插入。同样 O(n log n),便于扩展到更一般的大于关系。
代码实现
java
import java.util.*;
class Solution {
// 解法一:归并排序
public int reversePairs(int[] nums) {
return mergeSort(nums, 0, nums.length - 1);
}
private int mergeSort(int[] nums, int l, int r) {
if (l >= r) return 0;
int mid = l + (r - l) / 2;
int cnt = mergeSort(nums, l, mid) + mergeSort(nums, mid + 1, r);
int j = mid + 1;
for (int i = l; i <= mid; ++i) {
while (j <= r && (long) nums[i] > 2L * nums[j]) ++j;
cnt += j - (mid + 1);
}
int[] tmp = new int[r - l + 1];
int p = l, q = mid + 1, k = 0;
while (p <= mid && q <= r) {
if (nums[p] <= nums[q]) tmp[k++] = nums[p++];
else tmp[k++] = nums[q++];
}
while (p <= mid) tmp[k++] = nums[p++];
while (q <= r) tmp[k++] = nums[q++];
for (int i = 0; i < tmp.length; ++i) nums[l + i] = tmp[i];
return cnt;
}
// 解法二:树状数组 + 离散化
public int reversePairs2(int[] nums) {
int n = nums.length;
long[] vals = new long[2 * n];
int idx = 0;
for (int x : nums) { vals[idx++] = x; vals[idx++] = 2L * x; }
Arrays.sort(vals);
int m = 0;
long[] uniq = new long[2 * n];
for (int i = 0; i < vals.length; ++i) {
if (i == 0 || vals[i] != vals[i - 1]) uniq[m++] = vals[i];
}
Fenwick bit = new Fenwick(m);
int ans = 0;
for (int x : nums) {
int r = rankLE(uniq, m, 2L * x); // 已插入中值 <= 2x 的个数
ans += bit.query(m) - bit.query(r); // 值 > 2x 的个数
bit.add(rankLE(uniq, m, x), 1);
}
return ans;
}
private int rankLE(long[] uniq, int m, long v) {
int lo = 0, hi = m - 1, ans = 0;
while (lo <= hi) {
int mid = (lo + hi) / 2;
if (uniq[mid] <= v) { ans = mid + 1; lo = mid + 1; }
else hi = mid - 1;
}
return ans;
}
static class Fenwick {
int[] t;
Fenwick(int n) { t = new int[n + 1]; }
void add(int i, int d) { for (; i < t.length; i += i & -i) t[i] += d; }
int query(int i) { int s = 0; for (; i > 0; i -= i & -i) s += t[i]; return s; }
}
public static void main(String[] args) {
Solution sol = new Solution();
int pass = 0, total = 0;
++total;
if (sol.reversePairs(new int[]{1, 3, 2, 3, 1}) == 2) { ++pass; } else { System.out.println("FAIL test1: " + sol.reversePairs(new int[]{1, 3, 2, 3, 1})); }
++total;
if (sol.reversePairs(new int[]{2, 4, 3, 5, 1}) == 3) { ++pass; } else { System.out.println("FAIL test2: " + sol.reversePairs(new int[]{2, 4, 3, 5, 1})); }
++total;
if (sol.reversePairs2(new int[]{1, 3, 2, 3, 1}) == 2) { ++pass; } else { System.out.println("FAIL test3: " + sol.reversePairs2(new int[]{1, 3, 2, 3, 1})); }
++total;
if (sol.reversePairs2(new int[]{2, 4, 3, 5, 1}) == 3) { ++pass; } else { System.out.println("FAIL test4: " + sol.reversePairs2(new int[]{2, 4, 3, 5, 1})); }
if (pass == total) {
System.out.println("== 0493 PASS ==");
} else {
System.out.println("== 0493 FAIL ==");
System.exit(1);
}
}
}
复杂度分析
| 解法 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 解法一 | O(n log n) | O(n) |
| 解法二 | O(n log n) | O(n) |
494. Target Sum(目标和)
题目描述
给定非负整数数组 nums 与整数 target。给每个数前添加 + 或 - 并串联,返回运算结果等于 target 的不同表达式的数目。
示例
输入 `nums = [1,1,1,1,1]`,`target = 3`,输出 `5`
输入 `nums = [1]`,`target = 1`,输出 `1`
提示:
1 <= nums.length <= 20,0 <= nums[i] <= 1000,-1000 <= target <= 1000。
解题思路
解法一(DFS 回溯):对每个数枚举 +/- 两种符号,递归到底层时统计和等于 target 的方案数。直观但为指数级。
解法二(0-1 背包):设取正号的数和为 P、取负号的和为 N,有 P - N = target 且 P + N = sum。
联立得 P = (sum + target) / 2,问题转化为"从 nums 中选若干数使其和恰为 P"的子集数(0-1 背包计数)。
代码实现
java
import java.util.*;
class Solution {
// 解法一:DFS 回溯
public int findTargetSumWays(int[] nums, int target) {
return dfs(nums, 0, 0, target);
}
private int dfs(int[] nums, int i, int sum, int target) {
if (i == nums.length) return sum == target ? 1 : 0;
return dfs(nums, i + 1, sum + nums[i], target) + dfs(nums, i + 1, sum - nums[i], target);
}
// 解法二:0-1 背包
public int findTargetSumWays2(int[] nums, int target) {
int total = 0;
for (int x : nums) total += x;
if ((total + target) % 2 != 0 || total < -target) return 0;
int P = (total + target) / 2;
if (P < 0) return 0;
int[] dp = new int[P + 1];
dp[0] = 1;
for (int x : nums) {
for (int s = P; s >= x; --s) dp[s] += dp[s - x];
}
return dp[P];
}
public static void main(String[] args) {
Solution sol = new Solution();
int pass = 0, total = 0;
++total;
if (sol.findTargetSumWays(new int[]{1, 1, 1, 1, 1}, 3) == 5) { ++pass; } else { System.out.println("FAIL test1: " + sol.findTargetSumWays(new int[]{1, 1, 1, 1, 1}, 3)); }
++total;
if (sol.findTargetSumWays(new int[]{1}, 1) == 1) { ++pass; } else { System.out.println("FAIL test2"); }
++total;
if (sol.findTargetSumWays2(new int[]{1, 1, 1, 1, 1}, 3) == 5) { ++pass; } else { System.out.println("FAIL test3: " + sol.findTargetSumWays2(new int[]{1, 1, 1, 1, 1}, 3)); }
++total;
if (sol.findTargetSumWays2(new int[]{1}, 1) == 1) { ++pass; } else { System.out.println("FAIL test4"); }
if (pass == total) {
System.out.println("== 0494 PASS ==");
} else {
System.out.println("== 0494 FAIL ==");
System.exit(1);
}
}
}
复杂度分析
| 解法 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 解法一 | O(2^n) | O(n) |
| 解法二 | O(n * P) | O§ |
495. Teemo Attacking(提莫攻击)
题目描述
提莫攻击使艾希中毒 duration 秒(区间 [t, t+duration-1])。若中毒结束前再次被攻击,计时器重置。
给定非递减 的攻击时刻数组 timeSeries 与 duration,返回艾希处于中毒状态的总秒数。
示例
输入 `timeSeries = [1,4]`,`duration = 2`,输出 `4`
输入 `timeSeries = [1,2]`,`duration = 2`,输出 `3`
提示:
1 <= timeSeries.length <= 10^4,0 <= timeSeries[i], duration <= 10^7。
解题思路
解法一(相邻间隔取小):相邻两次攻击的间隔为 gap = timeSeries[i] - timeSeries[i-1];若 gap >= duration,
本次贡献完整 duration 秒,否则只贡献 gap 秒(后一次攻击在中毒结束前重置);最后一次固定贡献 duration。
代码实现
java
import java.util.*;
class Solution {
public int findPoisonedDuration(int[] timeSeries, int duration) {
if (timeSeries.length == 0) return 0;
int total = 0;
for (int i = 1; i < timeSeries.length; ++i) {
int gap = timeSeries[i] - timeSeries[i - 1];
total += Math.min(gap, duration);
}
total += duration;
return total;
}
public static void main(String[] args) {
Solution sol = new Solution();
int pass = 0, total = 0;
++total;
if (sol.findPoisonedDuration(new int[]{1, 4}, 2) == 4) { ++pass; } else { System.out.println("FAIL test1: " + sol.findPoisonedDuration(new int[]{1, 4}, 2)); }
++total;
if (sol.findPoisonedDuration(new int[]{1, 2}, 2) == 3) { ++pass; } else { System.out.println("FAIL test2: " + sol.findPoisonedDuration(new int[]{1, 2}, 2)); }
++total;
if (sol.findPoisonedDuration(new int[]{1, 2, 3, 4}, 5) == 8) { ++pass; } else { System.out.println("FAIL test3: " + sol.findPoisonedDuration(new int[]{1, 2, 3, 4}, 5)); }
if (pass == total) {
System.out.println("== 0495 PASS ==");
} else {
System.out.println("== 0495 FAIL ==");
System.exit(1);
}
}
}
复杂度分析
| 解法 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 解法一 | O(n) | O(1) |
496. Next Greater Element I(下一个更大元素 I)
题目描述
nums1 中数字 x 的下一个更大元素是指 x 在 nums2 中对应位置右侧 第一个比 x 大的元素。给定无重复元素的 nums1(nums2 的子集)与 nums2,
返回一个数组 ans,ans[i] 为 nums1[i] 的下一个更大元素,不存在则为 -1。
示例
输入 `nums1 = [4,1,2]`,`nums2 = [1,3,4,2]`,输出 `[-1,3,-1]`
输入 `nums1 = [2,4]`,`nums2 = [1,2,3,4]`,输出 `[3,-1]`
提示:
1 <= nums1.length <= nums2.length <= 1000,两数组元素互不相同。
解题思路
解法一(单调栈 + 哈希表):对 nums2 从左到右用单调递减栈求出每个元素的"下一个更大元素"并存入 map;
再遍历 nums1 直接查表。时间 O(m + n),满足进阶要求。也可对每个 nums1[i] 在 nums2 中暴力右扫(O(m n)),但效率较低。
代码实现
java
import java.util.*;
class Solution {
public int[] nextGreaterElement(int[] nums1, int[] nums2) {
Map<Integer, Integer> map = new HashMap<>();
Deque<Integer> stack = new ArrayDeque<>();
for (int x : nums2) {
while (!stack.isEmpty() && stack.peek() < x) {
map.put(stack.pop(), x);
}
stack.push(x);
}
int[] res = new int[nums1.length];
for (int i = 0; i < nums1.length; ++i) {
res[i] = map.getOrDefault(nums1[i], -1);
}
return res;
}
public static void main(String[] args) {
Solution sol = new Solution();
int pass = 0, total = 0;
++total;
if (Arrays.equals(sol.nextGreaterElement(new int[]{4, 1, 2}, new int[]{1, 3, 4, 2}), new int[]{-1, 3, -1})) { ++pass; } else { System.out.println("FAIL test1: " + Arrays.toString(sol.nextGreaterElement(new int[]{4, 1, 2}, new int[]{1, 3, 4, 2}))); }
++total;
if (Arrays.equals(sol.nextGreaterElement(new int[]{2, 4}, new int[]{1, 2, 3, 4}), new int[]{3, -1})) { ++pass; } else { System.out.println("FAIL test2: " + Arrays.toString(sol.nextGreaterElement(new int[]{2, 4}, new int[]{1, 2, 3, 4}))); }
++total;
if (Arrays.equals(sol.nextGreaterElement(new int[]{1, 3, 5, 2, 4}, new int[]{6, 5, 4, 3, 2, 1, 7}), new int[]{7, 7, 7, 7, 7})) { ++pass; } else { System.out.println("FAIL test3: " + Arrays.toString(sol.nextGreaterElement(new int[]{1, 3, 5, 2, 4}, new int[]{6, 5, 4, 3, 2, 1, 7}))); }
if (pass == total) {
System.out.println("== 0496 PASS ==");
} else {
System.out.println("== 0496 FAIL ==");
System.exit(1);
}
}
}
复杂度分析
| 解法 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 解法一 | O(m + n) | O(n) |
497. Random Point in Non-overlapping Rectangles(非重叠矩形中的随机点)
题目描述
给定由非重叠轴对齐矩形数组 rects(rects[i] = [ai, bi, xi, yi] 表示左下角 (ai,bi) 与右上角 (xi,yi))。
实现 Solution 类:构造器用 rects 初始化;pick() 在全部矩形覆盖的整数点中等概率 随机返回一个点 [u, v](矩形周长上的整数点也算覆盖)。
示例
输入 `["Solution","pick","pick","pick","pick","pick"]`,`[[[[-2,-2,1,1],[2,2,4,6]]],[],[],[],[],[]]`
输出 `[null,[1,-2],[1,-1],[-1,-2],[-2,-2],[0,0]]`
提示:
1 <= rects.length <= 100,所有矩形不重叠,pick最多调用10^4次。
解题思路
解法一(前缀和 + 二分):先算每个矩形覆盖的整数点个数(面积 = (xi-ai+1)*(yi-bi+1)),做前缀和 prefix。
pick() 时先随机一个 [1, total] 的数,二分定位落在哪个矩形,再在该矩形内随机横纵坐标。由于按面积加权,每个整数点等概率。
代码实现
java
import java.util.*;
class Solution {
private int[][] rects;
private int[] prefix;
private int total;
private Random rand;
public Solution(int[][] rects) {
this.rects = rects;
prefix = new int[rects.length];
total = 0;
for (int i = 0; i < rects.length; ++i) {
int area = (rects[i][2] - rects[i][0] + 1) * (rects[i][3] - rects[i][1] + 1);
total += area;
prefix[i] = total;
}
rand = new Random();
}
public int[] pick() {
int r = rand.nextInt(total) + 1;
int lo = 0, hi = prefix.length - 1;
while (lo < hi) {
int mid = (lo + hi) / 2;
if (prefix[mid] < r) lo = mid + 1;
else hi = mid;
}
int[] rect = rects[lo];
int x = rect[0] + rand.nextInt(rect[2] - rect[0] + 1);
int y = rect[1] + rand.nextInt(rect[3] - rect[1] + 1);
return new int[]{x, y};
}
public static void main(String[] args) {
Solution sol = new Solution(new int[][]{{-2, -2, 1, 1}, {2, 2, 4, 6}});
int pass = 0, totalT = 0;
int ok = 0;
for (int i = 0; i < 5000; ++i) {
int[] p = sol.pick();
// 验证该点落在任一矩形覆盖范围内
boolean inside = false;
for (int[] rect : new int[][]{{-2, -2, 1, 1}, {2, 2, 4, 6}}) {
if (p[0] >= rect[0] && p[0] <= rect[2] && p[1] >= rect[1] && p[1] <= rect[3]) inside = true;
}
if (inside) ++ok;
}
++totalT;
if (ok == 5000) { ++pass; } else { System.out.println("FAIL test1: ok=" + ok); }
if (pass == totalT) {
System.out.println("== 0497 PASS ==");
} else {
System.out.println("== 0497 FAIL ==");
System.exit(1);
}
}
}
复杂度分析
| 解法 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 解法一 | O(log n) | O(n) |
498. Diagonal Traverse(对角线遍历)
题目描述
给定 m x n 矩阵 mat,以对角线遍历顺序返回所有元素的数组。遍历方向:第 0 条对角线向右上,第 1 条向左下,交替进行。
示例
输入 `mat = [[1,2,3],[4,5,6],[7,8,9]]`,输出 `[1,2,4,7,5,3,6,8,9]`
输入 `mat = [[1,2],[3,4]]`,输出 `[1,2,3,4]`
提示:
1 <= m, n <= 10^4,m * n <= 10^4。
解题思路
解法一(按对角线编号遍历):共有 m + n - 1 条对角线,编号为 s。s 为偶数时沿对角线从下往上(i 递减、j 递增),
s 为奇数时从上往下(i 递增、j 递减)。每条对角线的起点由 s 与边界决定。
代码实现
java
import java.util.*;
class Solution {
public int[] findDiagonalOrder(int[][] mat) {
int m = mat.length, n = mat[0].length;
int[] res = new int[m * n];
int k = 0;
for (int s = 0; s < m + n - 1; ++s) {
if (s % 2 == 0) {
int i = Math.min(s, m - 1);
int j = s - i;
while (i >= 0 && j < n) {
res[k++] = mat[i][j];
--i; ++j;
}
} else {
int j = Math.min(s, n - 1);
int i = s - j;
while (j >= 0 && i < m) {
res[k++] = mat[i][j];
++i; --j;
}
}
}
return res;
}
public static void main(String[] args) {
Solution sol = new Solution();
int pass = 0, total = 0;
++total;
if (Arrays.equals(sol.findDiagonalOrder(new int[][]{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}),
new int[]{1, 2, 4, 7, 5, 3, 6, 8, 9})) { ++pass; } else { System.out.println("FAIL test1: " + Arrays.toString(sol.findDiagonalOrder(new int[][]{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}))); }
++total;
if (Arrays.equals(sol.findDiagonalOrder(new int[][]{{1, 2}, {3, 4}}),
new int[]{1, 2, 3, 4})) { ++pass; } else { System.out.println("FAIL test2: " + Arrays.toString(sol.findDiagonalOrder(new int[][]{{1, 2}, {3, 4}}))); }
if (pass == total) {
System.out.println("== 0498 PASS ==");
} else {
System.out.println("== 0498 FAIL ==");
System.exit(1);
}
}
}
复杂度分析
| 解法 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 解法一 | O(m n) | O(1)(不计输出) |
499. The Maze III(迷宫 III)
题目描述
同 0490 的滚动球迷宫,多了球洞 hole。球滚动直到撞墙前停下。求球能停在 hole 的最短滚动距离 路径;
若距离相同,返回字典序最小的方向序列(l < r < u < d)。无可行路径返回 "impossible"。
示例
`maze = [[0,0,0,0,0],[1,1,0,0,1],[0,0,0,0,0],[0,1,0,0,1],[0,1,0,0,0]]`,`ball = [4,3]`,`hole = [0,1]` → `"lul"`
同迷宫 `hole = [3,0]` → `"impossible"`
`maze = [[0,0,0,0,0,0,0],[0,0,1,0,0,1,0],[0,0,0,0,1,0,0],[0,0,0,0,0,0,1]]`,`ball = [0,4]`,`hole = [3,5]` → `"dldr"`
提示:
1 <= m, n <= 100。
解题思路
解法一(带字典序的最优 Dijkstra):把状态定义为 (dist, i, j, path)。用优先队列按 (dist, path) 排序。
从 ball 出发,朝 4 个方向滚动并记录方向字符,直到撞墙前的停点;若新距离更小或(距离相同且路径字典序更小)则松弛并入队。
优先队列保证首次弹出 hole 时即为「最短且字典序最小」的答案。
代码实现
java
import java.util.*;
class Solution {
private static final int[][] DIRS = {{-1, 0, 0}, {0, 1, 1}, {1, 0, 2}, {0, -1, 3}}; // 上、右、下、左
private static final String[] DC = {"u", "r", "d", "l"};
public String findShortestWay(int[][] maze, int[] ball, int[] hole) {
int m = maze.length, n = maze[0].length;
int[][] dist = new int[m][n];
String[][] best = new String[m][n];
for (int i = 0; i < m; ++i) Arrays.fill(dist[i], Integer.MAX_VALUE);
PriorityQueue<Node> pq = new PriorityQueue<>();
pq.offer(new Node(0, ball[0], ball[1], ""));
dist[ball[0]][ball[1]] = 0;
best[ball[0]][ball[1]] = "";
while (!pq.isEmpty()) {
Node cur = pq.poll();
if (cur.dist > dist[cur.i][cur.j]) continue;
if (best[cur.i][cur.j] != null && cur.path.compareTo(best[cur.i][cur.j]) > 0) continue;
if (cur.i == hole[0] && cur.j == hole[1]) return cur.path;
for (int d = 0; d < 4; ++d) {
int x = cur.i, y = cur.j, steps = 0;
while (x + DIRS[d][0] >= 0 && x + DIRS[d][0] < m
&& y + DIRS[d][1] >= 0 && y + DIRS[d][1] < n
&& maze[x + DIRS[d][0]][y + DIRS[d][1]] == 0) {
x += DIRS[d][0];
y += DIRS[d][1];
++steps;
if (x == hole[0] && y == hole[1]) break; // 进洞即停
}
int nd = cur.dist + steps;
String np = cur.path + DC[d];
if (nd < dist[x][y] || (nd == dist[x][y] && (best[x][y] == null || np.compareTo(best[x][y]) < 0))) {
dist[x][y] = nd;
best[x][y] = np;
pq.offer(new Node(nd, x, y, np));
}
}
}
return "impossible";
}
static class Node implements Comparable<Node> {
int dist, i, j;
String path;
Node(int d, int i, int j, String p) { dist = d; this.i = i; this.j = j; path = p; }
public int compareTo(Node o) {
if (dist != o.dist) return dist - o.dist;
return path.compareTo(o.path);
}
}
public static void main(String[] args) {
Solution sol = new Solution();
int pass = 0, total = 0;
int[][] maze1 = {
{0, 0, 0, 0, 0},
{1, 1, 0, 0, 1},
{0, 0, 0, 0, 0},
{0, 1, 0, 0, 1},
{0, 1, 0, 0, 0}
};
++total;
if ("lul".equals(sol.findShortestWay(maze1, new int[]{4, 3}, new int[]{0, 1}))) { ++pass; } else { System.out.println("FAIL test1: " + sol.findShortestWay(maze1, new int[]{4, 3}, new int[]{0, 1})); }
++total;
if ("impossible".equals(sol.findShortestWay(maze1, new int[]{4, 3}, new int[]{3, 0}))) { ++pass; } else { System.out.println("FAIL test2: " + sol.findShortestWay(maze1, new int[]{4, 3}, new int[]{3, 0})); }
int[][] maze3 = {
{0, 0, 0, 0, 0, 0, 0},
{0, 0, 1, 0, 0, 1, 0},
{0, 0, 0, 0, 1, 0, 0},
{0, 0, 0, 0, 0, 0, 1}
};
++total;
if ("dldr".equals(sol.findShortestWay(maze3, new int[]{0, 4}, new int[]{3, 5}))) { ++pass; } else { System.out.println("FAIL test3: " + sol.findShortestWay(maze3, new int[]{0, 4}, new int[]{3, 5})); }
if (pass == total) {
System.out.println("== 0499 PASS ==");
} else {
System.out.println("== 0499 FAIL ==");
System.exit(1);
}
}
}
复杂度分析
| 解法 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 解法一 | O(m n * 4 * 最大路径) | O(m n) |
500. Keyboard Row(键盘行)
题目描述
给定字符串数组 words,返回其中所有"只用美式键盘同一行字母"的单词(顺序任意)。键盘三行分别为 qwertyuiop / asdfghjkl / zxcvbnm。
示例
输入 `words = ["Hello","Alaska","Dad","Peace"]`,输出 `["Alaska","Dad"]`
输入 `words = ["omk"]`,输出 `[]`
输入 `words = ["adsdf","sfd"]`,输出 `["adsdf","sfd"]`
提示:
1 <= words.length <= 20,1 <= words[i].length <= 100,单词仅含英文字母。
解题思路
解法一(按行判定):对三行各建一个字符集合;对每个单词,取其首字母确定所属行,检查单词所有字母是否都在该行内,是则保留。
代码实现
java
import java.util.*;
class Solution {
public String[] findWords(String[] words) {
String[] rows = {"qwertyuiop", "asdfghjkl", "zxcvbnm"};
List<String> res = new ArrayList<>();
for (String w : words) {
if (w.isEmpty()) { res.add(w); continue; }
int row = rowOf(Character.toLowerCase(w.charAt(0)), rows);
boolean ok = true;
for (char c : w.toLowerCase().toCharArray()) {
if (rowOf(c, rows) != row) { ok = false; break; }
}
if (ok) res.add(w);
}
return res.toArray(new String[0]);
}
private int rowOf(char c, String[] rows) {
for (int i = 0; i < rows.length; ++i) {
if (rows[i].indexOf(c) >= 0) return i;
}
return -1;
}
public static void main(String[] args) {
Solution sol = new Solution();
int pass = 0, total = 0;
++total;
if (Arrays.equals(sol.findWords(new String[]{"Hello", "Alaska", "Dad", "Peace"}),
new String[]{"Alaska", "Dad"})) { ++pass; } else { System.out.println("FAIL test1: " + Arrays.toString(sol.findWords(new String[]{"Hello", "Alaska", "Dad", "Peace"}))); }
++total;
if (Arrays.equals(sol.findWords(new String[]{"omk"}), new String[]{})) { ++pass; } else { System.out.println("FAIL test2: " + Arrays.toString(sol.findWords(new String[]{"omk"}))); }
++total;
if (Arrays.equals(sol.findWords(new String[]{"adsdf", "sfd"}), new String[]{"adsdf", "sfd"})) { ++pass; } else { System.out.println("FAIL test3: " + Arrays.toString(sol.findWords(new String[]{"adsdf", "sfd"}))); }
if (pass == total) {
System.out.println("== 0500 PASS ==");
} else {
System.out.println("== 0500 FAIL ==");
System.exit(1);
}
}
}
复杂度分析
| 解法 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 解法一 | O(Σwordsi) | O(1) |