算法入门-字符串3

第二部分:字符串

43.字符串相乘(中等)

题目:给定两个以字符串形式表示的非负整数 num1num2,返回 num1num2 的乘积,它们的乘积也表示为字符串形式。

注意:不能使用任何内置的 BigInteger 库或直接将输入转换为整数。

示例 1:

输入: num1 = "2", num2 = "3"
输出: "6"

一开始看到题目确实有点懵😵,不过确实不能直接转整数,毕竟总不能直接转换成200位的整数吧~,想了一下没有什么好思路还是看了眼提示。实现两个大整数的相乘,数字被表示为字符串。使用字符串可以避免整数溢出问题,因为在数值超出常规整数范围的情况下,使用字符串表示比使用基本数据类型要安全得多。

第一种思路:

  1. 特殊情况处理

    • 首先检查任意一个输入数字是否等于 "0",如果是,直接返回 "0",因为两个数字相乘时只要有一个是零,结果就是零。

  2. 乘法计算的主体

    • 使用双重循环在两个数字间进行乘法。

    • 外层循环遍历 num2(第二个数字),内层循环遍历 num1(第一个数字)。由于是从最低位开始计算,因此两者都是从后往前遍历。

  3. 构建当前乘积

    • 对于 num2 当前的数字(yDigit),将其与 num1 中每一个数字(xDigit)相乘,并根据乘法的结果和进位(carry)计算当前乘积。

    • 将每次乘法的个位数添加到一个 StringBuilder 中,并更新进位。

  4. 处理进位

    • 如果存在进位(即 carry 不为零),在当前乘积的结果后添加该进位。

  5. 反转和累加

    • 由于在构建当前乘积时是从最低位到最高位构建的,所以需要将当前乘积字符串反转。

    • 将当前乘积结果通过调用 addStrings 方法与最终结果 result 相加,以便正确累加所有的乘积。

  6. 字符串加法处理

    • addStrings 方法负责处理两个输入字符串的加法。

    • 通过分别比较两个数字的位,逐位相加,并考虑进位,最后拼接相加结果。

  7. 最终输出

    • 在所有乘法和加法处理完成后,最终结果作为字符串形式输出。

class Solution {  
    public String multiply(String num1, String num2) {  
        // 如果任意一个数字为 "0",则结果为 "0"  
        if (num1.equals("0") || num2.equals("0")) {  
            return "0";  
        }  
        
        // 结果初始化为 "0"  
        String result = "0";  
        int len1 = num1.length(), len2 = num2.length();  
        
        // 遍历第二个数字,从最低位开始  
        for (int i = len2 - 1; i >= 0; i--) {  
            StringBuilder currentProduct = new StringBuilder();  
            int carry = 0; // 用于保存进位  
            
            // 对当前位进行乘法,添加相应的零  
            for (int zeroPadding = len2 - 1; zeroPadding > i; zeroPadding--) {  
                currentProduct.append(0);  
            }  
            
            // 将 num2 当前位转换为整数  
            int yDigit = num2.charAt(i) - '0';  
            
            // 遍历第一个数字,从最低位开始  
            for (int j = len1 - 1; j >= 0; j--) {  
                int xDigit = num1.charAt(j) - '0';  
                int product = xDigit * yDigit + carry;  
                
                // 将当前乘积的个位数添加到结果中  
                currentProduct.append(product % 10);  
                carry = product / 10; // 更新进位  
            }  
            
            // 如果还有进位,添加到当前结果  
            if (carry != 0) {  
                currentProduct.append(carry);  
            }  
            
            // 将当前的乘积字符串翻转,并累加到总结果中  
            result = addStrings(result, currentProduct.reverse().toString());  
        }  
        return result;  
    }  

    // 字符串加法方法  
    public String addStrings(String num1, String num2) {  
        int index1 = num1.length() - 1;  
        int index2 = num2.length() - 1;  
        int carry = 0; // 用于保存进位  
        StringBuilder sumResult = new StringBuilder();  
        
        // 当任一数字还有位数,或者有进位时继续计算  
        while (index1 >= 0 || index2 >= 0 || carry != 0) {  
            int xDigit = index1 >= 0 ? num1.charAt(index1) - '0' : 0;  
            int yDigit = index2 >= 0 ? num2.charAt(index2) - '0' : 0;  
            
            int total = xDigit + yDigit + carry;  
            sumResult.append(total % 10); // 添加个位数到结果中  
            carry = total / 10; // 更新进位  
            
            index1--;  
            index2--;  
        }  
        
        // 翻转结果字符串并返回  
        return sumResult.reverse().toString();  
    }  
}

第二种思路:

上面的思路官方叫做加法,下面思路叫做乘法,但是我觉得这两种思路其实也差不多,只是一个是取乘数的每一位,一个是取被乘数的每一位。

  1. 特殊情况处理

    • 在代码开始时,首先检查其中任一输入字符串是 "0"。如果是,直接返回 "0",因为任何数与零相乘都为零。

    if (num1.equals("0") || num2.equals("0")) {  
        return "0";  
    }  
  2. 初始化变量

    • 获取两个数字字符串的长度,并初始化一个用于存储乘积的数组 productArray。该数组的长度为 len1 + len2,因为两个数字相乘,结果最多可以是这两个长度的总和。

    int len1 = num1.length();  
    int len2 = num2.length();  
    int[] productArray = new int[len1 + len2]; // 存储乘积的数组  
  3. 手动乘法模拟

    • 使用两个嵌套的 for 循环,分别从最后一位开始遍历 num1num2,模拟竖式乘法的过程。

    • 计算数字 digit1digit2 的乘积,并将其累加到 productArray 中合适的位置(i + j + 1)。此位置计算的方式可以确保每个乘积被放在正确的位数上:

    for (int i = len1 - 1; i >= 0; i--) {  
        int digit1 = num1.charAt(i) - '0'; // 提取 num1 的当前数字  
        for (int j = len2 - 1; j >= 0; j--) {  
            int digit2 = num2.charAt(j) - '0'; // 提取 num2 的当前数字  
            int product = digit1 * digit2; // 计算当前乘积  
            productArray[i + j + 1] += product; // 累加乘积到合适的位置  
        }  
    }  
  4. 处理进位

    • 产品数组可能包含值大于 9 的元素,因此需要处理进位。通过一个循环从 len1 + len2 - 1 向前遍历,对每个位置检查是否有进位(即如果数字大于 9),将其进位值加到前面的元素中,并更新当前元素的值为个位数:

    for (int k = len1 + len2 - 1; k > 0; k--) {  
        productArray[k - 1] += productArray[k] / 10; // 将进位加到前一位  
        productArray[k] %= 10; // 当前位只保留个位数  
    }  
  5. 构建结果字符串

    • 确定结果的起始位置,如果 productArray[0]0,则从 1 开始;否则从 0 开始。这样可以跳过前导零。

    • 利用 StringBuilder 构建最终的结果字符串,逐位添加 productArray 中的数字:

    int startIndex = productArray[0] == 0 ? 1 : 0;  
    StringBuilder result = new StringBuilder();  
    while (startIndex < len1 + len2) {  
        result.append(productArray[startIndex]); // 添加每一位数字  
        startIndex++;  
    }  
  6. 返回结果

    • 最后,返回构建的结果字符串。通过这种方式,我们得到了两个大数字的乘积。

最后,其实这里的代码逻辑可以稍微记住,通过模拟手动乘法的方式,巧妙地处理了大数字相乘的问题,避免了直接使用数据类型可能导致的溢出,为每个乘法操作和进位处理提供了明确的逻辑。这使得代码能够正确处理任意大的数字(只要它们以字符串的形式提供)。

对了,这里再说明下,上面有个需要证明的地方我是直接当结论记住了: 令 m 和 n 分别表示 num 1和 num 2的长度,并且它们均不为 0,则 num 1和 num 2的乘积的长度为 m+n−1 或 m+n。

class Solution {
    public String multiply(String num1, String num2) {
        // 特殊情况,当任一数字为 "0" 时,返回 "0"
        if (num1.equals("0") || num2.equals("0")) {
            return "0";
        }

        int len1 = num1.length();
        int len2 = num2.length();
        int[] productArray = new int[len1 + len2]; // 存储乘积的数组

        // 从后往前遍历 num1 和 num2 进行乘法计算
        for (int i = len1 - 1; i >= 0; i--) {
            int digit1 = num1.charAt(i) - '0'; // 提取 num1 的当前数字
            for (int j = len2 - 1; j >= 0; j--) {
                int digit2 = num2.charAt(j) - '0'; // 提取 num2 的当前数字
                int product = digit1 * digit2; // 计算当前乘积
                // 累加乘积到合适的位置
                productArray[i + j + 1] += product;
            }
        }

        // 处理进位
        for (int k = len1 + len2 - 1; k > 0; k--) {
            productArray[k - 1] += productArray[k] / 10; // 将进位加到前一位
            productArray[k] %= 10; // 当前位只保留个位数
        }

        // 查找结果的起始位置,跳过前导零
        int startIndex = productArray[0] == 0 ? 1 : 0;
        StringBuilder result = new StringBuilder();

        // 构造最终结果字符串
        while (startIndex < len1 + len2) {
            result.append(productArray[startIndex]); // 添加每一位数字
            startIndex++;
        }

        return result.toString(); // 返回结果字符串
    }
}

累了累了,要准备比赛去了~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值