AI刷题题解(一) | 豆包MarsCode AI刷题

118 阅读7分钟

找单独的数之题解

题目概述

在一个班级中,每位同学都拿到了一张卡片,上面有一个整数。有趣的是,除了一个数字之外,所有的数字都恰好出现了两次。现在需要你帮助班长小C快速找到那个拿了独特数字卡片的同学手上的数字是什么。

要求:

  1. 设计一个算法,使其时间复杂度为 O(n),其中 n 是班级的人数。
  2. 尽量减少额外空间的使用,以体现你的算法优化能力,并编写对该算法的测试用例。

限制是

  • 设计一个算法,使其时间复杂度为 O(n),其中n是数集元素的个数
  • 1≤ cards.ength ≤ 1001
  • 0≤ cards[i]≤ 1000
  • 集合元素个数为奇数
  • 除了一个数字只出现一次外,其余每个数字都恰好出现两次

解题思路

  • 数据结构选择 因为题目要求时间复杂度为 O(n),并且尽量减少额外空间的使用,所以考虑使用位运算来解题。
  • 算法选择
  1. 异或运算 应用异或运算的重要性质,a^a=0,a^0=a(会在后面展开解释),该性质可以保证对集合中所有元素依次进行异或运算后,得到的结果就是在寻找的特殊数
  2. 遍历数组 对数组进行遍历,确定没有遗漏的未参与异或运算的数字
  3. 最后把结果值返回

实现代码

public class Main{
    public static int solution(int[] cards) {
        int result = 0;
        for (int card : cards) {
            result ^= card;
        }
        return result;
    }
    public static void main(String[] args) {
        System.out.println(solution(new int[]{7, 3, 3, 7, 10}) == 10);
        System.out.println(solution(new int[]{1, 1, 2, 2, 3, 3, 4, 5, 5}) == 4);
        System.out.println(solution(new int[]{0, 1, 0, 1, 2}) == 2);
       }
}  

输出范例

   true
   true
   true

增强for循环的语法

for (元素类型 变量名 : 数组或集合) { // 循环体 }

具体运行过程

  • 假设数组 cards 为测试用例里的 [7, 3, 3, 7, 10]: result 初始值为 0。

  • 增强for循环遍历数组:

  1. 第一次:card = 7,result = 0 ^ 7 = 7
  2. 第二次:card = 3,result = 7 ^ 3 = 4
  3. 第三次:card = 3,result = 4 ^ 3 = 7
  4. 第四次:card = 7,result = 7 ^ 7 = 0
  5. 第五次:card = 10,result = 0 ^ 10 = 10
  • 最终 result 的值为 10,即数组中唯一出现一次的元素。
  • 增强for循环:for (int card : cards) 表示遍历数组 cards 中的每个元素,并将每个元素赋值给变量 card。
  • 异或运算:result ^= card; 表示将 result 和当前数组元素 card 进行异或运算,并更新 result。

result ^= card; 将 result 和当前数组元素 card 进行异或运算,并更新 result。

增强for循环自动遍历数组中的每个元素,不需要手动管理索引。

异或运算的性质(引用部分摘自网络)

  • 相同为0,不同为1:异或运算的规则是,如果两个位相同,结果为0;如果两个位不同,结果为1。
  • 自反性:a ^ a = 0,即任何数与自身异或的结果为0。
  • 恒等性:a ^ 0 = a,即任何数与0异或的结果为它本身。
  • 如果一个数与a进行了两次异或运算,那么结果是这个数它本身。
  • 1. 归零律:a ⊕ a = 0
  • 2. 恒等律:a ⊕ 0 = a
  • 3. 交换律:a ⊕ b = b ⊕ a
  • 4. 结合律:a ⊕b ⊕ c = a ⊕ (b ⊕ c) = (a ⊕ b) ⊕ c
  • 5. 自反:a ⊕ b ⊕ a = b
    1. d = a ⊕ b ⊕ c 可以推出 a = d ⊕ b ⊕ c
  • 7.若x是二进制数0101,y是二进制数1011; 则x⊕y=****1110
  • 只有在两个比较的位不同时其结果是1,否则结果为0

测试用例的说明

考虑三种情况,即

  • 其他数字成对连续出现,特殊数单独出现在数组的末尾或开头
  • 其他数字成对连续出现,特殊数在某两对数字之间
  • 其他数字乱序出现,特殊数在任意位置

写在第一题后的tips

  • 位运算的含义:对二进制位进行操作的运算

常见的位运算符

  • 按位与 &:两个位都为1时,结果为1,否则为0。
  • 按位或 |:两个位中只要有一个为1,结果为1,否则为0。
  • 按位异或 ^:两个位相同为0,不同为1。
  • 按位取反 ~:将每一位取反,1变为0,0变为1。
  • 左移 <<:将二进制位向左移动指定的位数,右边补0。
  • 右移 >>:将二进制位向右移动指定的位数,左边补符号位(正数补0,负数补1)。
  • 无符号右移 >>>:将二进制位向右移动指定的位数,左边补0。
  • 位运算的优点
  1. 位运算通常比算术运算更快
  2. 使用位来表示状态,节省空间

找出整型数组中占比超过一半的数之题解

题目概述

小R从班级中抽取了一些同学,每位同学都会给出一个数字。已知在这些数字中,某个数字的出现次数超过了数字总数的一半。现在需要你帮助小R找到这个数字。

限制是

  • 整型数组,只考虑整数

解题思路

解决此题可以考虑数组的一个重要性质:

在数组中,出现次数超过一半的元素(称为“多数元素”),其出现次数比其他所有元素的出现次数之和还要多。

  • 摩尔投票算法就是基于这个性质设计的算法。
  1. 摩尔投票算法: 摩尔投票算法是一种用于寻找数组中多数元素的经典算法,核心思想是通过抵消的方式来找到多数元素。

算法步骤如下:

  1. 初始化一个候选元素 candidate 和计数器 count
  2. 遍历数组中的每个元素:
  • 如果 count 为 0,将当前元素设为 candidate
  • 如果当前元素与 candidate 相同,则 count 加 1。
  • 如果当前元素与 candidate 不同,则 count 减 1。
  1. 最终,candidate 就是多数元素。

2.验证多数元素

  • 虽然摩尔投票算法能够找到一个候选元素,但为了确保它是多数元素,我们还需要再次遍历数组,统计该候选元素的出现次数,确认其出现次数确实超过数组长度的一半。

实现代码

public class Main {
    public static int solution(int[] array) {
        int candidate = 0;
        int count = 0;
        // 摩尔投票算法
        for (int num : array) {
            if (count == 0) {
                candidate = num;
            }
            if (num == candidate) {
                count++;
            } else {
                count--;
            }
        }
        count = 0;
        for (int num : array) {
            if (num == candidate) {
                count++;
            }
        }
        
        if (count > array.length / 2) {
            return candidate;
        } else {
            throw new IllegalArgumentException("No majority element found");
        }
    }

    public static void main(String[] args) {
        System.out.println(solution(new int[]{5, 5, 5, 1, 2, 5, 5}) == 5);
        System.out.println(solution(new int[]{1, 3, 8, 2, 3, 1, 3, 3, 3}) == 3);
    }
}

输出范例

true
true

具体运行过程

  1. 初始化

    • candidate 初始化为 0。
    • count 初始化为 0。
  2. 摩尔投票算法

    • 遍历数组中的每个元素 num(此处亦使用了增强for循环):

      • 如果 count 为 0,将当前元素 num 设为 candidate
      • 如果当前元素 num 与 candidate 相同,则 count 加 1。
      • 如果当前元素 num 与 candidate 不同,则 count 减 1。
  3. 验证候选元素

    • 再次遍历数组,统计 candidate 的出现次数。
    • 如果 candidate 的出现次数超过数组长度的一半,则返1. - 回 candidate
    • 否则,抛出异常 IllegalArgumentException,表示没有找到多数元素。

测试用例的运行过程

以测试用例 [1, 3, 8, 2, 3, 1, 3, 3, 3] 为例:

  1. 初始化

    • candidate = 0
    • count = 0
  2. 摩尔投票算法

    • 遍历数组:

      • num = 1count = 0candidate = 1count = 1
      • num = 3count = 1candidate = 1count = 0
      • num = 8count = 0candidate = 8count = 1
      • num = 2count = 1candidate = 8count = 0
      • num = 3count = 0candidate = 3count = 1
      • num = 1count = 1candidate = 3count = 0
      • num = 3count = 0candidate = 3count = 1
      • num = 3count = 1candidate = 3count = 2
      • num = 3count = 2candidate = 3count = 3

    最终,candidate = 3count = 3

  3. 验证候选元素

    • 再次遍历数组,统计 candidate = 3 的出现次数:

      • count = 5
    • 检查 count > array.length / 2

      • 5 > 9 / 2,即 5 > 4,条件成立。
    • 返回 candidate = 3

测试用例的说明

  • 尽可能多写几种不同的测试用例,以保障程序的正确
  • 注意数据类型,整数