第三天刷题:数字字符串格式化 | 豆包MarsCode AI刷题

43 阅读4分钟

一、题目解析

在这道题目中,我们面对的是一个包含数字的数组。每个数组元素被视为一个字符串,我们可以从每个字符串中选出一个字符组成一个新数。目标是使最终得到的数字的各个位之和为偶数,并且统计所有满足此条件的组合个数。

题目的关键点在于:

  1. 将每个数字分解成单个字符(每一位)。
  2. 遍历每个数字的所有位数组合,找出满足位数和为偶数的组合数量。
  3. 使用递归(回溯)来枚举所有可能的组合。

二、解题思路

  1. 分解问题:首先,将每个整数视为一个字符串,以便逐位访问每一个数字。这样可以灵活地从每一位数中选出一个数字。
  2. 递归回溯:利用递归逐步构建每一位的组合。通过递归的 backTrack 函数,逐步从每一个数字中选取一个位数,并在最终判断构成的组合是否满足条件(位数和为偶数)。
  3. 判断条件:在组合完成后(递归到数组末尾),检查组合的各位和是否为偶数,如果是,则计数器加一。
  4. 清理全局状态:由于 resultpath 是全局变量,每次递归结束后需要清理这些变量的状态,以便适应多次调用。 三、代码详解 import java.util.*;

public class Main { static int result = 0; static ArrayList path = new ArrayList();

public static int solution(int[] numbers) {
    // 调用回溯方法
    backTrack(numbers, 0);
    int r = result; // 记录当前结果
    result = 0; // 清空结果,方便下次调用
    path.clear(); // 清空路径
    return r;
}

// 回溯方法
public static void backTrack(int[] numbers, int index) {
    // 递归出口:当index达到数组长度
    if (index == numbers.length) {
        if (valid(path)) { // 检查是否满足条件
            result++; // 满足条件则计数器加一
            return;
        } else {
            return;
        }
    }

    // 当前数字的每一位都作为单独的选择
    String str = String.valueOf(numbers[index]);
    for (int i = 0; i < str.length(); i++) {
        char c = str.charAt(i);
        path.add(Character.getNumericValue(c)); // 将字符转为数字并加入路径
        backTrack(numbers, index + 1); // 递归下一个数字
        path.remove(path.size() - 1); // 回溯:移除当前选择
    }
}

// 检查路径是否满足条件(和为偶数)
public static boolean valid(ArrayList<Integer> arr) {
    int sum = 0;
    for (int i = 0; i < arr.size(); i++) {
        sum += arr.get(i);
    }
    return sum % 2 == 0; // 返回是否为偶数
}

public static void main(String[] args) {
    // 测试用例
    System.out.println(solution(new int[] { 123, 456, 789 }) == 14);
    System.out.println(solution(new int[] { 123456789 }) == 4);
    System.out.println(solution(new int[] { 14329, 7568 }) == 10);
}

}

四、代码分析

  • 全局变量

    • result:记录符合条件的组合数。
    • path:存储当前递归路径,用于组合各个位数。
  • 主函数 solution(int[] numbers)

    • 负责调用 backTrack 递归方法进行组合计算,并返回最终的 result 值。
    • 每次调用 solution 后,resultpath 会被重置,以便为下一次调用提供干净的环境。
  • 回溯函数 backTrack(int[] numbers, int index)

    • 该方法通过递归逐步访问每一个数字的每一位,从每个数字中选择一位加入 path
    • 递归结束时,通过 valid 方法判断组合是否符合要求。
    • path.remove(path.size() - 1) 是回溯的关键步骤,确保在每层递归结束时清理当前选择。
  • 有效性检查函数 valid(ArrayList<Integer> arr)

    • 计算 path 中数字的和,判断是否为偶数。

五、示例分析

以输入 [123, 456, 789] 为例:

  1. 123 可以提供 1, 2, 3 三个选择。
  2. 456 提供 4, 5, 6
  3. 789 提供 7, 8, 9

通过回溯方法,可以生成 333 = 27 种组合,经过 valid 方法筛选,最终符合条件的组合数量为 14。

六、心得体会

  1. 回溯的灵活性:通过回溯可以轻松地生成所有可能的组合,这对题目中的数字组合问题非常适用。
  2. 全局状态清理:由于 resultpath 是全局变量,设计时需要在每次计算结束后清空状态,以防影响下一次调用。
  3. 问题分解:将问题分解成单个数字的组合,再用递归实现,简化了整体逻辑,使得代码更具条理性和扩展性。

七、改进方向

  • 该算法使用全局变量存储结果,在多线程或并发环境中可能会产生不一致的问题。可以考虑将 resultpath 改为局部变量,并使用函数返回值传递结果,以提高代码的可重用性和稳定性。
  • 此外,对于更大的数据集,算法的时间复杂度会迅速上升。可以引入动态规划或剪枝优化,提前排除不可能符合条件的组合,减少递归深度。