LeetCode476~500题目解析

LeetCode 476-500. 题解合集

文章目录


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)
相关推荐
雨落在了我的手上1 小时前
JavaEE初级(1):计算机是如何⼯作的
java·java-ee
惊讶的猫1 小时前
Spring AOP
java·开发语言
用户3126874877201 小时前
Java 异常体系全链路:受检异常到底该不该用
java
学到老活到老1 小时前
集合换成线程安全的,就不用加锁了吗?
java·面试
Wang's Blog2 小时前
Java 项目实战: 交友项目 - 今日佳人:需求分析
java·开发语言·交友
余额瞒着我当琳2 小时前
C++11,发展历史浅谈,初始化列表和对象初始化,右值引用和移动语义
java·开发语言·c++·算法
曹牧2 小时前
Java:`tools.jar`
java·开发语言·jar
IT枫斗者枫哥2 小时前
Spring AI文档更新后仍命中旧内容:核对分片ID、来源和版本
java·人工智能