leetcode
leetcode 1101 ~ 1150
形成三的最大倍数

形成三的最大倍数

难度:

标签:

题目描述

代码结果

运行时间: 36 ms, 内存: 16.4 MB


/*
 * 题目思路:
 * 1. 计算所有数字的总和。
 * 2. 如果总和是3的倍数,那么按降序排列所有数字并返回结果。
 * 3. 如果总和不是3的倍数,计算余数并尝试移除最少的数字使得总和变为3的倍数。
 * 4. 如果无法移除数字使得总和变为3的倍数,则返回空字符串。
 * 5. 处理前导零的情况,最终返回结果。
 */

import java.util.*;
import java.util.stream.*;

public class LargestMultipleOfThreeStream {
    public String largestMultipleOfThree(int[] digits) {
        int sum = Arrays.stream(digits).sum();
        Map<Integer, Long> count = Arrays.stream(digits).boxed()
                .collect(Collectors.groupingBy(d -> d, Collectors.counting()));
        List<Integer> list = Arrays.stream(digits).boxed()
                .sorted(Collections.reverseOrder())
                .collect(Collectors.toList());
        if (sum % 3 == 1) {
            if (!removeDigit(count, 1)) {
                removeDigit(count, 2);
                removeDigit(count, 2);
            }
        } else if (sum % 3 == 2) {
            if (!removeDigit(count, 2)) {
                removeDigit(count, 1);
                removeDigit(count, 1);
            }
        }
        String result = count.entrySet().stream()
                .sorted(Map.Entry.<Integer, Long>comparingByKey().reversed())
                .flatMap(e -> Collections.nCopies(e.getValue().intValue(), e.getKey()).stream())
                .map(String::valueOf)
                .collect(Collectors.joining());
        if (result.length() > 0 && result.charAt(0) == '0') return "0";
        return result;
    }

    private boolean removeDigit(Map<Integer, Long> count, int mod) {
        for (int i = mod; i < 10; i += 3) {
            if (count.getOrDefault(i, 0L) > 0) {
                count.put(i, count.get(i) - 1);
                return true;
            }
        }
        return false;
    }

    public static void main(String[] args) {
        LargestMultipleOfThreeStream solution = new LargestMultipleOfThreeStream();
        int[] digits1 = {8, 1, 9};
        int[] digits2 = {8, 6, 7, 1, 0};
        int[] digits3 = {1};
        int[] digits4 = {0, 0, 0, 0, 0, 0};
        System.out.println(solution.largestMultipleOfThree(digits1)); // "981"
        System.out.println(solution.largestMultipleOfThree(digits2)); // "8760"
        System.out.println(solution.largestMultipleOfThree(digits3)); // ""
        System.out.println(solution.largestMultipleOfThree(digits4)); // "0"
    }
}

解释

方法:

首先通过计数排序的思路统计每个数字出现的次数,然后计算所有数字的总和。如果总和是3的倍数,那么就可以直接用这些数字组成最大的数。如果不是3的倍数,需要通过删除几个最小的数字来调整总和使其成为3的倍数。具体来说: 1. 计算总和模3的结果,如果余数为1,尝试先删除一个余数为1的数字,如果没有,再尝试删除两个余数为2的数字;如果余数为2,尝试先删除一个余数为2的数字,如果没有,再尝试删除两个余数为1的数字。 2. 删除数字后,按照从大到小的顺序组合剩余的数字,将结果转化为字符串。如果数字中包含0,则确保0只在最后出现,除非结果只有0。 3. 特别注意处理只有0或结果无法形成3的倍数的情况。

时间复杂度:

O(n)

空间复杂度:

O(1)

代码细节讲解

🦆
算法中计算总和时为什么只考虑模3不为0的部分?模3为0的数字对总和有什么影响?
计算总和时主要关注模3不为0的部分是因为模3为0的数字本身就是3的倍数,不会影响总和是否为3的倍数。将这些数字加入总和中,总和的模3的结果不会改变。因此,关键在于如何处理那些模3后余1和余2的数字,通过适当的添加或移除这些数字,来使总和成为3的倍数。
🦆
在处理余数为1时,为何首选删除一个余数为1的数字,而非两个余数为2的数字?这样的选择对结果有何优势?
首选删除一个余数为1的数字是因为这样可以最小化对数字总数的影响,从而尽可能保持结果数字的大和长。删除两个余数为2的数字虽然也可以达到使总和成为3的倍数的效果,但这样会损失更多的数字,可能导致结果数字变小。优先删除较少数量的数字有助于最大化最终形成的数字。
🦆
如果在尝试删除指定数量的余数为1或2的数字时没有足够的数字可供删除,这种情况如何处理,会直接返回空字符串吗?
如果没有足够的数字可以删除来调整使总和成为3的倍数,那么无法形成有效的3的倍数。在这种情况下,如果输入中还包含其他数字,可以尝试重新组合其他的数字来形成最大的可能数字。如果只有不足以调整的余数1或2的数字,理论上会返回一个尽可能大的非3倍数数字,或者在某些实现中可能会返回空字符串,具体取决于题目要求和实现细节。
🦆
代码中存在`counter[i] -= 1 for i in mod1`的语法错误。应该如何正确实现在找到第一个可用数字后减少其计数器的值?
正确的实现方式是使用一个循环来遍历需要检查的数字,并在找到第一个可用的数字后立即减少其计数器的值并退出循环。例如,可以使用以下代码片段: found = False for i in mod1: if counter[i] > 0: counter[i] -= 1 found = True break if not found: # 实施其他策略,如删除两个余数为2的数字 这样可以确保只在找到第一个符合条件的数字时进行操作,并且可以在没有符合条件的数字时实施备用策略。

相关问题