LeetCode67(二进制求和[位运算,大数运算])

news2024/11/15 21:48:28

二进制求和

题目要求:
给你两个二进制字符串 a 和 b ,以二进制字符串的形式返回它们的和。

在这里插入图片描述
这道题其实有几种解法.我们先来介绍简单的方法.
我们可以将两个字符串的二进制转成十进制,获取对应值相加之后,我们可以不断对2取余,获取尾数拼接即可.也就是像我们平常求一个十进制的二进制数,可以递归调用,同样也可以迭代.官方题解当中给我们介绍了一种Java自带的API解法,如下所示

class Solution {
    public String addBinary(String a, String b) {
        return Integer.toBinaryString(
            Integer.parseInt(a, 2) + Integer.parseInt(b, 2)
        );
    }
}

但是这种解法具有局限性,官方提到:
如果 a 的位数是 n,b 的位数为 m,这个算法的渐进时间复杂度为 O(n+m)。但是这里非常简单的实现基于 Python 和 Java 本身的高精度功能,在其他的语言中可能并不适用,并且在 Java 中:

  • 如果字符串超过 33 位,不能转化为 Integer
  • 如果字符串超过 65 位,不能转化为 Long
  • 如果字符串超过 500000001 位,不能转化为 BigInteger

所以我们需要一个更加健全的解法
对于这两个字符串,我们可以对最末端,不断相加,若有进位,保留进位相加.一步步拼接字符串.由于是二进制的运算,我们很容易联系到位运算.

对于位运算来讲,十进制的相加我们可以模拟成(a b 相加)
无进位时: sum = a ^ b
有进位时: sum = a ^ b + (a & b) << 1

例如: a = 3 b = 1 时
我们将其转换为二进制
a: 11
b: 01
我们先进行异或运算可以求的没有进位的位相加的结果,这里我们可以得到
temp = 10;
其次我们来求得需要进位的位置,需要进位其实就是上下两端都等于1时,所以我们可以用&运算来实现我们的需求
求得 carry = 01;左移一位得到 carry = 10;
重复上述过程,直至(a & b) == 0即没有进位为止
temp ^ carry = 00;
temp & carry = 10;
左移相加得到 100;满足相加的结果.

所以我们初步的解题步骤为

我们需要比较两个字符串长度最小值选取为第一次循环的终止条件,然后从字符串的最后面开始遍历,不断对最后一位进行异或运算.代码为

		int size = Math.min(aLength,bLength);
		int carry = 0;
        StringBuilder sb = new StringBuilder();
        for(int i = 0;i < size;i++){
            int x = a.charAt(--aLength)-'0';
            int y = b.charAt(--bLength)-'0';
            int sum = (x ^ y ^ carry);
            carry = ( x & y) ; 
            sb.append(sum);
        }

这段代码其实是有一点问题的,我们思考下面这种情形.
当x = 1 , y = 0 ,carry = 1时
我们的 carry 计算公式为 = x & y
这样将会错过进位的运算.
那会提出三个数做异或运算呗,像那个sum一样
注意:
我们sum求值概念为只要存在两个1,sum处一定是0,因为对应的是那一位的运算,二进制逢二进一,所以我们对三个数做异或运算就可以满足此特性.
但是此时进位处和其不太一样
我们需要只要出现两个1,我们的进位就应该是1.
那我们先来思考x与y的取值有几种情形
x = 1 y = 0
x = 0 y = 1
x = 0 y = 0
x = 1 y = 1

我们讨论的情况当carry = 1时.属于x,y中最多只有一个数出现为1时,我们需要进位 置1.也就是我们进行xy的异或运算后再进行对carry的&运算.

而对于carry = 0而且x与y都等于1时,我们不需要carry进行对应的运算.
所以我们列出改进后的代码

		for(int i = 0;i < size;i++){
            int x = a.charAt(--aLength)-'0';
            int y = b.charAt(--bLength)-'0';
            int sum = (x ^ y ^ carry);
            //carry == 1 x==1 y==0 如果采用x&y 这里会错过进位
            if(carry == 1 && x!=1 && y!=1){
                carry = (x^y) & carry ;
            }
            else if(carry == 0) carry = ( x & y) ;
            sb.append(sum);
        }

此时我们已经完成了较短的字符串的二进制运算
而对于剩下的那个我们仍需要进行字符串的拼接,但我们需要注意的是
可能在第一次循环过后最后相加的那个结点处,我们产生了进位,所以我们依旧需要对进位进行处理.

		while (aLength > 0){
            int x = a.charAt(--aLength)-'0';
            int sum = (x ^ carry);
            carry = ((x&carry));
            sb.append(sum);
        }
        while (bLength > 0){
            int y = b.charAt(--bLength)-'0';
            int sum = (y ^ carry);
            carry = ((y&carry));
            sb.append(sum);
        }

看样子好像已经挺完善啦,其实我们还是漏了一点.也就是当两个字符串都遍历完后.如果此时最高位产生了进位,我们是需要扩展原来的长度,即再加一位来存放最高处的进位.
所以完整代码为

class Solution {
    public String addBinary(String a, String b) {
        int aLength = a.length();
        int bLength = b.length();
        if(aLength < 1) return b;
        if(bLength < 1) return a;
        int carry = 0;
        int size = Math.min(aLength,bLength);
        StringBuilder sb = new StringBuilder();
        for(int i = 0;i < size;i++){
            int x = a.charAt(--aLength)-'0';
            int y = b.charAt(--bLength)-'0';
            int sum = (x ^ y ^ carry);
            //carry == 1 x==1 y==0 如果采用x&y 这里会错过进位
            if(carry == 1 && x!=1 && y!=1){
                carry = (x^y) & carry ;
            }
            else if(carry == 0) carry = ( x & y) ;
            sb.append(sum);
        }
        while (aLength > 0){
            int x = a.charAt(--aLength)-'0';
            int sum = (x ^ carry);
            carry = ((x&carry));
            sb.append(sum);
        }
        while (bLength > 0){
            int y = b.charAt(--bLength)-'0';
            int sum = (y ^ carry);
            carry = ((y&carry));
            sb.append(sum);
        }
        if(carry > 0) sb.append(carry);
        return sb.reverse().toString();
    }
}

结果为:

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1909677.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

记一次线上流量突增问题排查

一.问题 接流量告警出现获取 xx 信息接口调用次数同比往年大促活动猛涨.扩大至 10 倍之多.心里顿时咯噔一下.最近各种严打,顶风作案.某不是摸到电门了.一下子要把自己带走.从此走向求职之路.一时间脑子哇哇的思绪万千. 202x.5.20 大促开门红的调用.这个是往年活动的时候的调用…

01-引论-操作系统的目标和作用

操作系统的目标 1.方便性 2.有效性 3.可扩充性 4.开放性 操作系统的目标与应用环境有关 在不同的应用环境下&#xff0c;操作系统的重点和功能可能会有所不同。例如&#xff0c;对于桌面操作系统&#xff0c;用户界面的友好性和多媒体功能可能是重点&#xff1b;对于服务…

Qt/C++项目积累: 2.主机监控器 - 2.1 项目介绍

修改记录 序号日期说明对应软件版本号120240709对预期功能和已完成功能进行新增无 一&#xff1a;项目主体编写背景 在观察程序的运行状态时&#xff0c;其对系统的CPU&#xff0c;内存&#xff0c;硬盘占用无疑是几项重要参考指标&#xff0c;而现有的监控软件&#xff0c;搜…

2024-07-08 base SAS programming学习笔记10(read data)

1.读入SAS 数据集 格式如下&#xff1a; data sas-data-set; set sas-data-set; run; sas-data-set 是数据集名称&#xff0c;可以是libname.filename的形式 举例如下&#xff1a; 2.在DATA步使用BY 语句 BY variable语句使用方法&#xff1a; a.必须在PROC SORT里面进行排序…

国产芯片四大流派,你最看好哪一条?

曾经&#xff0c;我们以为“地球村”是大势所趋&#xff0c;大家取长补短&#xff0c;合作共赢。 然而&#xff0c;2018年开始的那一场断供&#xff0c;让芯片自主从一个产业的隐忧变成了我国的一个“明伤”。近几年“卡脖子”事件屡屡上演&#xff0c;“缺芯”、“芯痛”成为…

Redis连接Resp图形化工具和springboot

Redis连接Resp图形化工具和springboot 1.redis配置1.1 备份、修改conf文件1.2 Redis的其它常见配置&#xff1a;1.3 启动Redis&#xff1a;1.4 停止服务&#xff1a;1.5 开机自启&#xff1a; 2. resp的安装、配置和连接&#xff1a;2.1 GitHub上下载2.2 开始连接redis ![在这里…

【Dison夏令营 Day 13】使用 Python 创建扫雷游戏

在本文中&#xff0c;我们将介绍如何使用 Python 语言创建自己的基于终端的扫雷程序。 关于游戏 1992年4月6日&#xff0c;扫雷和纸牌、空当接龙等小游戏搭载在Windows 3.1系统中与用户见面&#xff0c;主要目的是让用户训练使用鼠标。扫雷是一款单人游戏&#xff0c;这个游戏…

单例模式(大话设计模式)C/C++版本

单例模式 C 饿汉 /* HM hungry man 饿汉 */ #include <iostream> using namespace std; class Singleton { private:Singleton() { cout << "单例对象创建&#xff01;" << endl; };Singleton(const Singleton &);Singleton &operator(c…

app: 和 android:的区别

人不走空 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌赋&#xff1a;斯是陋室&#xff0c;惟吾德馨 目录 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌…

如何切换手机的ip地址

在数字时代的浪潮中&#xff0c;智能手机已成为我们日常生活中不可或缺的一部分。然而&#xff0c;随着网络安全问题的日益凸显&#xff0c;保护个人隐私和数据安全变得尤为重要。其中&#xff0c;IP地址作为网络身份的重要标识&#xff0c;其安全性与隐私性备受关注。本文将详…

百度旋转验证码

声明(lianxi a15018601872) 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;抓包内容、敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01; 前言(lianxi a…

OpenWrt入门 (1) - 登录及ssh命令接入wifi

本文参考自: [OpenWrt 维基]在 OpenWrt 上启用 Wi-Fi 接入点 --- [OpenWrt Wiki] Enabling a Wi-Fi access point on OpenWrt 需要详细了解的小伙伴请看原文 基本概念 OpenWrt是适用于嵌入式设备的一个Linux发行版。 相对原厂固件而言&#xff0c;OpenWrt不是一个单一、静态…

手写简单实现IOC

这个小demo是利用反射从最基础一步一步模拟实现了IOC的功能,所有的代码基本都给出了注释,方便大家阅读. 目录结构&#xff1a; 这里需要导入一下junit依赖 <!-- junit测试 --><dependency><groupId>junit</groupId><artifactId>junit</artif…

string类(STL开始)

相信大家都知道STL在C中的重要性&#xff0c;作为其模板库中的一部分&#xff0c;包含了常见的数据结构和算法&#xff0c;是C的标准库 而我们今天要讲的String类&#xff08;String底层是一个字符顺序数组的顺序表对象&#xff0c;可以归类为容器&#xff09;&#xff0c;其实…

JavaWeb系列二十二: 线程数据共享和安全(ThreadLocal)

韩顺平-线程数据共享和安全ThreadLocal 什么是ThreadLocal?ThreadLocal环境搭建ThreadLocal快速入门ThreadLocal源码阅读threadLocal.set()源码threadLocal.get()源码 什么是ThreadLocal? ThreadLocal的作用: 可以实现在同一个线程数据共享, 从而解决多线程数据安全问题.Thr…

一.6 存储设备形成层次结构

在处理器和一个较大的较慢的设备&#xff08;例如主存&#xff09;之间插入一个更小更快的存储设备&#xff08;例如高速缓存&#xff09;的想法已经成为一个普遍的概念。实际上&#xff0c;每个计算机系统重的存储设备都被组织成了一个存储器层次结构&#xff0c;如图1-9所示。…

楼梯导航案例

楼梯导航 <!DOCTYPE html> <html lang"zh-cn"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>滚动到展示区</title><link re…

Python 中将字典内容保存到 Excel 文件使用详解

概要 在数据处理和分析的过程中,经常需要将字典等数据结构保存到Excel文件中,以便于数据的存储、共享和进一步分析。Python提供了丰富的库来实现这一功能,其中最常用的是pandas和openpyxl。本文将详细介绍如何使用这些库将字典内容保存到Excel文件中,并包含具体的示例代码…

NodeJS小饰品销售管理系统-计算机毕业设计源码21597

摘 要 在当今的数字化时代&#xff0c;电子商务已经成为了商业领域中不可或缺的一部分。随着消费者对于购物体验的要求越来越高&#xff0c;一个高效、便捷、用户友好的小饰品销售管理系统显得尤为重要。 本系统旨在利用 JavaScript 技术&#xff0c;设计并实现一个功能强大的小…

Python实现动态银河系:模拟旋转的银河动画

文章目录 引言准备工作前置条件 代码实现与解析导入必要的库初始化Pygame定义星系类主循环 完整代码 引言 银河系的旋转动画是一个迷人且富有挑战性的项目。通过模拟星系的旋转&#xff0c;我们可以更好地理解天文学现象&#xff0c;并创造出视觉上令人惊叹的效果。在这篇博客…