【经典算法】LeetCode 67. 二进制求和(Java/C/Python3/Golang实现含注释说明,Easy)

简介: 【经典算法】LeetCode 67. 二进制求和(Java/C/Python3/Golang实现含注释说明,Easy)

题目描述

给定两个二进制字符串,返回它们的和(也表示为二进制字符串)。

输入为两个非空的二进制字符串,只包含字符 ‘0’ 或 ‘1’。

原题:LeetCode 67

思路及实现

方式一:模拟手工加法

思路

我们可以通过模拟手工加法的过程来解决这个问题。从两个二进制字符串的末尾开始,逐位相加,并处理可能出现的进位。如果两个字符串长度不同,则需要在较短的字符串前面补0,使其长度与较长的字符串相等。最后,将得到的二进制结果反转,即为所求的和。

代码实现

Java版本
public class Solution {
    public String addBinary(String a, String b) {
        StringBuilder sb = new StringBuilder();
        int carry = 0; // 进位
        int i = a.length() - 1;
        int j = b.length() - 1;
        while (i >= 0 || j >= 0 || carry != 0) {
            int sum = carry;
            if (i >= 0) {
                sum += a.charAt(i) - '0';
                i--;
            }
            if (j >= 0) {
                sum += b.charAt(j) - '0';
                j--;
            }
            sb.append(sum % 2); // 当前位的值
            carry = sum / 2; // 更新进位
        }
        // 反转字符串得到正确的结果
        return sb.reverse().toString();
    }
}

说明:

  • 使用StringBuilder来构建结果字符串,因为StringBuilderappend操作比String的拼接操作更高效。
  • 初始化进位carry为0。
  • 使用两个指针ij分别指向两个字符串的末尾,然后逐步向前遍历。
  • 如果两个字符串长度不同,较短的字符串的指针会先到达其字符串开头的前面,此时只需要将另一个字符串的对应位与进位相加即可。
  • 将计算得到的当前位的值加到StringBuilder中,并更新进位。
  • 最后,将StringBuilder反转,并转换为字符串返回。
C语言版本
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char* addBinary(char* a, char* b) {
    int lenA = strlen(a);
    int lenB = strlen(b);
    int carry = 0;
    int maxLength = lenA > lenB ? lenA : lenB;
    char* result = (char*)malloc(maxLength + 2); // 多分配一位用于进位和字符串结尾'\0'
    int i = lenA - 1;
    int j = lenB - 1;
    int index = 0;
    while (i >= 0 || j >= 0 || carry != 0) {
        int sum = carry;
        if (i >= 0) {
            sum += a[i] - '0';
            i--;
        }
        if (j >= 0) {
            sum += b[j] - '0';
            j--;
        }
        result[index++] = sum % 2 + '0';
        carry = sum / 2;
    }
    result[index] = '\0'; // 字符串结尾
    // 反转结果字符串
    for (int start = 0, end = index - 1; start < end; start++, end--) {
        char temp = result[start];
        result[start] = result[end];
        result[end] = temp;
    }
    return result;
}

说明:

  • 使用动态内存分配为结果字符串分配空间。
  • 其他部分与Java版本类似,只是C语言需要手动管理内存。
Python3版本
class Solution:
    def addBinary(self, a: str, b: str) -> str:
        carry = 0
        i, j = len(a) - 1, len(b) - 1
        result = []
        while i >= 0 or j >= 0 or carry:
            sum_val
            = carry
            if i >= 0:
                sum_val += int(a[i])
                i -= 1
            if j >= 0:
                sum_val += int(b[j])
                j -= 1
            result.append(str(sum_val % 2))
            carry = sum_val // 2
        return ''.join(reversed(result))

说明:

  • 使用列表result来存储每一位的结果,最后反转并拼接成字符串。
  • Python中的字符串是不可变的,因此使用列表来处理每一位的相加。
Golang版本
package main
import (
  "fmt"
  "strconv"
  "strings"
)
func addBinary(a string, b string) string {
  carry := 0
  i, j := len(a)-1, len(b)-1
  var result strings.Builder
  for i >= 0 || j >= 0 || carry > 0 {
    sum := carry
    if i >= 0 {
      sum += int(a[i] - '0')
      i--
    }
    if j >= 0 {
      sum += int(b[j] - '0')
      j--
    }
    result.WriteByte(byte(sum%2 + '0'))
    carry = sum / 2
  }
  // 反转结果字符串
  resultBytes := result.String()
  for i, j := 0, len(resultBytes)-1; i < j; i, j = i+1, j-1 {
    resultBytes = strings.Replace(resultBytes, string(resultBytes[i])+string(resultBytes[j]), string(resultBytes[j])+string(resultBytes[i]), 1)
  }
  return resultBytes
}
func main() {
  a := "1010"
  b := "1011"
  fmt.Println(addBinary(a, b)) // 输出: 10101
}

说明:

  • 使用strings.Builder来构建结果字符串,它提供了高效的字符串拼接方法。
  • 反转结果字符串时,通过交换字符串中的字符来实现。

复杂度分析

  • 时间复杂度:O(max(m, n)),其中m和n分别是两个输入字符串的长度。我们最多遍历两个字符串中的所有字符一次。
  • 空间复杂度:O(max(m, n)),用于存储结果字符串。在最坏情况下,两个字符串都是全1,相加后的结果长度会增加一位。

方式二:使用内建函数

思路

代码实现

使用内建函数进行二进制加法在Java、C,Python3和Golang中也是可行的,但需要注意的是,不同的语言有不同的内建函数和语法来处理字符串和整数之间的转换,以及整数的二进制表示。以下是如何在这些语言中使用内建函数进行二进制加法的示例。

Java版本
public class Solution {
    public String addBinary(String a, String b) {
        int num1 = Integer.parseInt(a, 2); // 将二进制字符串转换为整数
        int num2 = Integer.parseInt(b, 2);
        int sum = num1 + num2;
        return Integer.toBinaryString(sum); // 将整数转换为二进制字符串
    }
}

注意:Java的Integer.parseIntInteger.toBinaryString方法分别用于将字符串解析为整数(给定基数)和将整数转换为二进制字符串。但请注意,如果二进制字符串表示的数字超出了int类型的范围(即大于Integer.MAX_VALUE或小于Integer.MIN_VALUE),则这种方法会失败。对于更大的数,可能需要使用BigInteger类。

C版本

在C中,没有直接的内建函数可以将二进制字符串转换为整数或将整数转换为二进制字符串,因此你需要手动实现这些功能。但是,你可以使用标准库中的函数来辅助处理字符串和整数。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
char* addBinary(char* a, char* b) {
    // 转换字符串到整数(手动实现,因为没有内建函数)
    int len_a = strlen(a);
    int len_b = strlen(b);
    int max_len = len_a > len_b ? len_a : len_b;
    int carry = 0;
    int sum = 0;
    int *result = (int *)malloc((max_len + 2) * sizeof(int)); // +2 for carry and null terminator
    result[max_len + 1] = '\0'; // Null terminator for the string
    // 从字符串的末尾开始相加,并处理进位
    for (int i = 0; i <= max_len; i++) {
        int digit_a = i < len_a ? a[len_a - i - 1] - '0' : 0;
        int digit_b = i < len_b ? b[len_b - i - 1] - '0' : 0;
        sum = digit_a + digit_b + carry;
        result[max_len - i] = sum % 2;
        carry = sum / 2;
    }
    // 如果最高位有进位,则需要添加一位
    if (carry > 0) {
        result = (int *)realloc(result, (max_len + 2 + 1) * sizeof(int));
        memmove(result + 1, result, (max_len + 2) * sizeof(int));
        result[0] = carry;
        result[max_len + 2] = '\0'; // Update null terminator position
    }
    // 将整数数组转换为字符串并返回
    char *binary_string = (char *)malloc((max_len + 2 + 1) * sizeof(char)); // +1 for null terminator
    for (int i = 0; i <= max_len + carry; i++) {
        binary_string[i] = result[max_len + carry - i] + '0';
    }
    binary_string[max_len + carry + 1] = '\0'; // Null terminator
    free(result); // 释放整数数组的内存
    return binary_string;
}

注意:这个C语言实现手动处理了二进制字符串到整数的转换、加法运算以及结果整数到字符串的转换。没有使用内建函数来直接执行这些操作,因为C标准库没有提供这样的功能。

Python3版本

在Python3中,我们可以将二进制字符串转换为整数,进行加法运算后再转回二进制字符串。这种方法简单直接,但可能不是最优的,因为涉及到整数的转换。

class Solution:
    def addBinary(self, a: str, b: str) -> str:
        return bin(int(a, 2) + int(b, 2))[2:]

说明:

  • int(a, 2)将二进制字符串a转换为整数。
  • bin()函数将整数转换为二进制字符串,但前缀为0b,因此使用切片[2:]去掉前缀。
Golang版本
在Golang中,使用`strconv`包中的函数将二进制字符串转换为整数,并执行加法运算后,再将结果转换回二进制字符串,是一个简单且有效的方法。下面是完整的Golang代码示例:
package main
import (
  "fmt"
  "strconv"
)
func addBinary(a string, b string) string {
  // 将二进制字符串转换为整数
  num1, _ := strconv.ParseInt(a, 2, 64)
  num2, _ := strconv.ParseInt(b, 2, 64)
  // 执行加法运算
  sum := num1 + num2
  // 将整数转换回二进制字符串
  binarySum := strconv.FormatInt(sum, 2)
  return binarySum
}
func main() {
  a := "1010"
  b := "1011"
  fmt.Println(addBinary(a, b)) // 输出: 10101
}

注意:strconv.ParseInt函数用于将字符串解析为指定基数的整数,这里我们使用基数为2(二进制)。strconv.FormatInt函数用于将整数格式化为指定基数的字符串。

在这个例子中,我们假设输入的二进制字符串表示的数值不会超过int64类型的范围。如果输入的数值可能超出这个范围,你需要使用big.Int类型来处理大整数。但是,对于大多数常见的二进制字符串加法问题,使用int64类型通常是足够的。

使用内建函数的好处是代码简洁且易于理解,但缺点是当处理大数时可能会受到限制。如果你需要处理任意长度的二进制数,那么可能需要手动实现加法算法,就像前面C语言示例中所做的那样。然而,在Golang中,使用big.Int可以很容易地处理大整数,同时保持代码的可读性和简洁性。

复杂度分析

当分析内置函数用于二进制加法操作的复杂度时,我们需要区分时间复杂度和空间复杂度。时间复杂度关注的是算法执行所需的时间,而空间复杂度关注的是算法执行所需的额外空间(不包括输入数据本身所占用的空间)。

以下是以表格形式展示Java、C、Python 3和Golang中内置函数用于二进制加法操作的时间复杂度和空间复杂度分析:

语言 时间复杂度 空间复杂度 备注
Java O(n) O(1) int类型有固定位数(32位)
C O(n) O(1) 需要手动实现解析和转换
Python 3 O(n) O(n) 整数类型动态大小,字符串转换占用额外空间
Golang O(n) O(1) int64类型有固定位数(64位)

时间复杂度分析

  • 对于Java、C和Golang,解析二进制字符串为整数和将整数转换回二进制字符串的时间复杂度都是O(n),其中n是二进制字符串的长度。这是因为这两个操作都需要遍历字符串的每一位。
  • 整数加法的时间复杂度是O(1),因为这是一个固定时间的操作,不依赖于输入的大小(在固定位数的整数范围内)。

空间复杂度分析

  • 对于Java和Golang,空间复杂度是O(1)。这是因为这些语言中的整数类型是固定大小的,不需要额外的空间来存储中间结果。解析和转换操作通常只涉及对原有内存的修改,不需要分配额外的空间。
  • 对于C语言,虽然代码需要手动实现,但空间复杂度仍然是O(1),假设我们直接在原字符串上进行修改或者不使用额外的数据结构。
  • 对于Python 3,空间复杂度是O(n)。这是因为Python的字符串是不可变的,解析和转换操作通常会创建新的字符串对象,这些对象需要额外的空间来存储。此外,Python中的整数类型是动态大小的,但在这个特定场景下,整数的大小通常不会超过输入字符串的长度,因此整数本身不会显著增加空间复杂度。

需要注意的是,这里的空间复杂度分析是基于常见情况下的估计,并且假设没有其他额外的数据结构或变量被使用。在实际应用中,根据具体的实现方式和使用的数据结构,空间复杂度可能会有所不同。

总结

方式 优点 缺点 时间复杂度 空间复杂度
方式一 不依赖内建函数,灵活性强 代码量较大,实现相对复杂 O(max(m, n)) O(max(m, n))
方式二 代码简洁,易于理解 依赖内建函数,可能不是最优解 O(n) O(n) 或者O(1)

相似题目

相似题目 难度 链接
leetcode 369 给出数字在排序数组中的索引 简单 力扣:[力扣-369](https://leetcode-
相关文章
|
3月前
|
JavaScript 前端开发 Java
通义灵码 Rules 库合集来了,覆盖Java、TypeScript、Python、Go、JavaScript 等
通义灵码新上的外挂 Project Rules 获得了开发者的一致好评:最小成本适配我的开发风格、相当把团队经验沉淀下来,是个很好功能……
916 103
|
8月前
|
人工智能 安全 Java
Java和Python在企业中的应用情况
Java和Python在企业中的应用情况
231 7
|
4月前
|
Java API Docker
在线编程实现!如何在Java后端通过DockerClient操作Docker生成python环境
以上内容是一个简单的实现在Java后端中通过DockerClient操作Docker生成python环境并执行代码,最后销毁的案例全过程,也是实现一个简单的在线编程后端API的完整流程,你可以在此基础上添加额外的辅助功能,比如上传文件、编辑文件、查阅文件、自定义安装等功能。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
在线编程实现!如何在Java后端通过DockerClient操作Docker生成python环境
|
4月前
|
Java 编译器
课时7:Java程序基本概念(注释)
课时7介绍了Java程序中的注释。编程语言有其语法和语义,注释有助于理解代码需求,防止断档。Java支持三类注释:单行(//)、多行(/* */)和文档注释(/** */)。注释不会被编译器编译。范例中展示了如何在代码中使用注释,并强调了注释对项目文档管理的重要性。
|
6月前
|
算法 Java API
Java 方法注释:规范、实用和高质量的写法
本文深入探讨了如何编写高质量的 Java 方法注释
267 11
|
7月前
|
Java
Java 中的注释
1. 单行注释:// 2. 多行注释:/* */ 3. 文档注释::/** **/
|
8月前
|
Java 程序员 开发工具
在比较Java和Python哪个更易学
在比较Java和Python哪个更易学
91 4
|
1月前
|
机器学习/深度学习 算法 数据挖掘
基于WOA鲸鱼优化的BiLSTM双向长短期记忆网络序列预测算法matlab仿真,对比BiLSTM和LSTM
本项目基于MATLAB 2022a/2024b实现,采用WOA优化的BiLSTM算法进行序列预测。核心代码包含完整中文注释与操作视频,展示从参数优化到模型训练、预测的全流程。BiLSTM通过前向与后向LSTM结合,有效捕捉序列前后文信息,解决传统RNN梯度消失问题。WOA优化超参数(如学习率、隐藏层神经元数),提升模型性能,避免局部最优解。附有运行效果图预览,最终输出预测值与实际值对比,RMSE评估精度。适合研究时序数据分析与深度学习优化的开发者参考。
|
19天前
|
机器学习/深度学习 算法 数据安全/隐私保护
基于PSO粒子群优化的BiLSTM双向长短期记忆网络序列预测算法matlab仿真,对比BiLSTM和LSTM
本项目基于MATLAB2022a/2024b开发,结合粒子群优化(PSO)算法与双向长短期记忆网络(BiLSTM),用于优化序列预测任务中的模型参数。核心代码包含详细中文注释及操作视频,涵盖遗传算法优化过程、BiLSTM网络构建、训练及预测分析。通过PSO优化BiLSTM的超参数(如学习率、隐藏层神经元数等),显著提升模型捕捉长期依赖关系和上下文信息的能力,适用于气象、交通流量等场景。附有运行效果图预览,展示适应度值、RMSE变化及预测结果对比,验证方法有效性。
|
24天前
|
算法 JavaScript 数据安全/隐私保护
基于遗传算法的256QAM星座图的最优概率整形matlab仿真,对比优化前后整形星座图和误码率
本内容展示了基于GA(遗传算法)优化的256QAM概率星座整形(PCS)技术的研究与实现。通过Matlab仿真,分析了优化前后星座图和误码率(BER)的变化。256QAM采用非均匀概率分布(Maxwell-Boltzman分布)降低外圈星座点出现频率,减小平均功率并增加最小欧氏距离,从而提升传输性能。GA算法以BER为适应度函数,搜索最优整形参数v,显著降低误码率。核心程序实现了GA优化过程,包括种群初始化、选择、交叉、变异等步骤,并绘制了优化曲线。此研究有助于提高频谱效率和传输灵活性,适用于不同信道环境。
45 10

热门文章

最新文章

推荐镜像

更多