C++之大数运算

溪云初起日沉阁

山雨欲来风满楼


契子✨ 


我们知道数据类型皆有范围,一旦超出了这个范围就会造成溢出问题

今天说说我们常见的数据类型范围:

我们平时写代码也会遇到数据类型范围溢出问题:

比如 ~ 我们之前写的学生管理系统在用 int类型 填写学生学号时,我们发现了数据溢出

那么我们是怎么解决的呢?我们采用了 char* 类型 (数组)在我们 C++ 中也就是 string 类型

也就是我们可以用字符串类型存储较大的数(会溢出的数据)

而字符串的数学运算也被称为大数运算


废话不多说,铁铁们请看题:

字符串相加

题目链接:字符串相加


给定两个字符串形式的非负整数 num1 和num2 ,计算它们的和并同样以字符串形式返回。

你不能使用任何內建的用于处理大整数的库(比如 BigInteger), 也不能直接将输入的字符串转换为整数形式。

示例 1:

输入:num1 = "11", num2 = "123"
输出:"134"

示例 2:

输入:num1 = "456", num2 = "77"
输出:"533"

示例 3:

输入:num1 = "0", num2 = "0"
输出:"0"

提示:

  • 1 <= num1.length, num2.length <= 104
  • num1 和num2 都只包含数字 0-9
  • num1 和num2 都不包含任何前导零

题目分析: 

我们只需要对两个大整数模拟「竖式加法」的过程:

竖式加法就是我们平常学习生活中常用的对两个整数相加的方法,回想一下我们在纸上对两个整数相加的操作,是不是如下图将相同数位对齐,从低到高逐位相加,如果当前位和超过 10,则向高位进一位?我们只需按照这个思路来解决问题即可。

算法思想:

现在我们要考虑是从前往后加,还是从后往前加呢?如果是从前往后加则需要补 0 对齐,我们这里选择从后往前加。先定义两个指针 left、right 指向两个字符串 s1、s2 的末尾,再取出对应位置字符串的字符转化成 int整型 进行操作。最后将结果在转化为字符型并插入到字符串中。

注意:这里需要考虑进位问题,我们可以采用一个 next 变量来进行进位维护

如果 s1[left]+s2[right] >= 10,这个时候就需要进位了,简单暴力一点:

            next = (s1[left]+s2[right])/10;(s1[left]+s2[right]) %= 10;

对于我们的上一位数,我们选择直接加上 next 即可

代码测试:

class Solution 
{
public:string addStrings(string num1, string num2) {int end1 = num1.size()-1;int end2 = num2.size()-1;string str;int next = 0;while(end1>=0 || end2>=0) //最长的字符串遍历完就结束{//转化成 int 类型进行数学运算,如果前面没有数据就自动补 0int x1 = end1>=0 ? num1[end1--] - '0':0;int x2 = end2>=0 ? num2[end2--] - '0':0;int x = x1+x2+next;//判断进位next = x/10;x %= 10;//转化成字符在进行头插str.insert(str.begin(),'0'+x);}if(next == 1)str.insert(str.begin(),'1');return str;}
};

 

注意:

        if(next == 1)str.insert(str.begin(),'1');

我们来看这个代码,为什么要加上这一行代码呢?

如果 x1 = 1,x2 = 9 那么只有 '0' 被插入字符串,因为最长字符串的长度为 1 只能进行一次循环,当退出循环时还没来的及进位就结束了!!!

时间复杂度分析:

设两字符串中最长的长度为 n,那么时间复杂度为 O(n^2)

因为一次遍历 + 头插,为了让时间效率更优一点,优化成 O(n),我们可以改成尾插:

class Solution 
{
public:string addStrings(string num1, string num2) {int end1 = num1.size()-1;int end2 = num2.size()-1;string str;int next = 0;while(end1>=0 || end2>=0){int x1 = end1>=0 ? num1[end1--] - '0':0;int x2 = end2>=0 ? num2[end2--] - '0':0;int x = x1+x2+next;next = x/10;x %= 10;str.push_back('0'+x);}if(next == 1)str.push_back('1');reverse(str.begin(), str.end());return str;}
};

字符串相乘

题目链接:字符串相乘


给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式。

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

示例 1:

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

示例 2:

输入: num1 = "123", num2 = "456"
输出: "56088"

提示:

  • 1 <= num1.length, num2.length <= 200
  • num1 和 num2 只能由数字组成。
  • num1 和 num2 都不包含任何前导零,除了数字0本身。

题目分析: 

<1>首先可以做一个小小的优化:先判断两个字符串是否含有 "0" ,如果有直接返回 "0" 即可

<2>如果两个字符串都不是 "0" ,则可以通过模拟「竖式乘法」的方法计算乘积。从右往左遍历乘数,将乘数的每一位与被乘数相乘得到对应的结果,再将每次得到的结果累加

 <3>假设 num1 和 num2 的长度分别为 n 和 m,则它们的乘积的长度最多为 n + m

我们可以申请一个长度为 m + n 的数组,用于存储乘积的每一位

从低位到高位,依次计算乘积的每一位,最后将数组转换为字符串即可

注意:判断最高位是否为 0,如果是,则去掉

代码展示: 

class Solution 
{
public:string multiply(string num1, string num2) {if(num1 == "0"||num2 == "0")return "0";int n = num1.size(),m = num2.size();vector<int> arr(n+m);for(int i = n-1;i>=0;i--){for(int j = m-1;j>=0;j--){int a = num1[i] - '0';int b = num2[j] - '0';arr[i+j+1] += a*b;}}for(int i = arr.size()-1;i>0;i--){arr[i-1] += arr[i]/10;arr[i] %= 10;}int i = 0;while(arr[i] == 0){i++;}string str;for(i;i<arr.size();i++){str += '0'+arr[i];}return str;}
};

 

注意:

arr[i+j+1] += a*b;

因为 i = n-1 和 j = m-1 即 i+j = n+m-2,所以现在的右边界为 n+m-1,左边界为 0,所以长度是 n+m ,没有越界

 

 注意:

int i = 0;
while(arr[i] == 0)
{i++;
}

判断最高位是否为 0,如果是,则去掉

有些时候的 num1 和 num2 并没有到达 m+n 的长度,但是前位就会自动补 "0",所以要判断

 

时间复杂度分析:

假设 n 是 num1 的长度,m 是 num2 的长度,时间复杂度为 O(m×n) -- 两层 for 循环

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MySQL日志机制【undo log、redo log、binlog 】

前言 SQL执行流程图文分析&#xff1a;从连接到执行的全貌_一条 sql 执行的全流程?-CSDN博客文章浏览阅读1.1k次&#xff0c;点赞20次&#xff0c;收藏12次。本文探讨 MySQL 执行一条 SQL 查询语句的详细流程&#xff0c;从连接器开始&#xff0c;逐步介绍了查询缓存、解析 S…

【3dmax笔记】027:配置修改器集、工具栏自定义与加载

文章目录 一、配置修改器集二、自定义工具栏三、加载工具栏 一、配置修改器集 可以把自己常用的修改命令放到右边框中的部分&#xff0c;便于自己的操作&#xff0c;省去了每次都要花半天时间找命令的尴尬。新建一个二维或者三维物体&#xff0c;点击修改面板&#xff0c;点击…

Unity技术学习:渲染大量物体的解决方案,外加RenderMesh、RenderMeshInstanced、RenderMeshIndirect的简单使用

叠甲&#xff1a;本人比较菜&#xff0c;如果哪里不对或者有认知不到的地方&#xff0c;欢迎锐评&#xff08;不玻璃心&#xff09;&#xff01; 导师留了个任务&#xff0c;渲染大量的、移动的物体。 寻找解决方案&#xff1a; 当时找了几个解决方案&#xff1a; 静态批处…

《编译原理》阅读笔记:p1-p3

《编译原理》学习第 1 天&#xff0c;p1-p3总结&#xff0c;总计 3 页。 一、技术总结 1.compiler(编译器) p1, But, before a program can be run, it first must be translated into a form in which it can be executed by a computer. The software systems that do thi…

c++多线程2小时速成

简介 c多线程基础需要掌握这三个标准库的使用&#xff1a;std::thread,std::mutex, andstd::async。 1. Hello, world #include <iostream> #include <thread>void hello() { std::cout << "Hello Concurrent World!\n"; }int main() {std::th…

C++学习————第十天(string的基本使用)

1、string 对象类的常见构造 (constructor)函数名称 功能说明&#xff1a; string() &#xff08;重点&#xff09; 构造空的string类对象&#xff0c;即空字符串 string(const char* s) &#xff08;重点&#xff09;…

初识C++ · 模板初阶

目录 1 泛型编程 2 函数模板 3 类模板 1 泛型编程 模板是泛型编程的基础&#xff0c;泛型我们碰到过多次了&#xff0c;比如malloc函数返回的就是泛型指针&#xff0c;需要我们强转。 既然是泛型编程&#xff0c;也就是说我们可以通过一个样例来解决类似的问题&#xff0c…

【linux】进程概念|task_struct|getpid|getppid

目录 ​编辑 1.进程的概念 进程的基本概念 进程与程序的主要区别 进程的特征 进程的状态 描述进程—PCB task_struct中的内容 查看进程 1.创建一个进程&#xff0c;运行以下代码 通过系统调用获取进程标示符 getpid()以及getppid() 1.进程的概念 进程的基本概念…

rust容器、迭代器

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家&#xff1a;点击跳转 目录 一&#xff0c;std容器 1&#xff0c;Vec&#xff08;向量、栈&#xff09; 2&#xff0c;VecDeque&#xff08;队列、双端队…

C++类和对象(基础篇)

前言&#xff1a; 其实任何东西&#xff0c;只要你想学&#xff0c;没人能挡得住你&#xff0c;而且其实学的也很快。那么本篇开始学习类和对象&#xff08;C的&#xff0c;由于作者有Java基础&#xff0c;可能有些东西过得很快&#xff09;。 struct在C中的含义&#xff1a; …

开机弹窗找不到OpenCL.dll是怎么回事,哪种修复方法更推荐

当用户在操作电脑过程中遇到系统提示“OpenCL.dll丢失”时&#xff0c;这究竟是怎么一回事呢&#xff1f;OpenCL.dll&#xff0c;作为Open Computing Language&#xff08;开放计算语言&#xff09;的重要动态链接库文件&#xff0c;它在图形处理器&#xff08;GPU&#xff09;…

爬虫:爬取豆瓣电影

文章目录 前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据总结 前言 上篇我们将到如何利用xpath的规则&#xff0c;那么这一次&#xff0c;我们将通过案例来告诉读者如何使用Xpath来定位到我们需要的数据&#xff0c;就算你不懂H5代码是怎么个嵌套或者十分复…

my-room-in-3d中的电脑,电视,桌面光带发光原理

1. my-room-in-3d中的电脑&#xff0c;电视&#xff0c;桌面光带发光原理 最近在github中&#xff0c;看到了这样的一个项目&#xff1b; 项目地址 我看到的时候&#xff0c;蛮好奇他这个光带时怎么做的。 最后发现&#xff0c;他是通过&#xff0c;加载一个 lightMap.jpg这个…

C++语法|如何写出高效的C++代码(一)|对象使用过程中背后调用了哪些方法(构造和析构过程)?

文章目录 再探拷贝构造函数和重载复制运算符实例化新对象和赋值操作强转为类类型指针和引用时临时对象的构造和析构过程 考考你问题答案 再探拷贝构造函数和重载复制运算符 实例化新对象和赋值操作 首先我们写一个类&#xff0c;实现它的拷贝构造并重载赋值运算符。 class T…

数值计算方法——大题题型总结

目录 一、绝对误差限、相对误差限 1.1 例题 1.2 解题套路 1.3 题解 二、敛散性、收敛速度 2.1 例题 2.2 解题套路 2.3 题解 三、牛顿迭代法 3.1 例题 3.2 解题套路 3.3 题解 四、割线法 4.1 例题 4.2 解题套路 ​4.3 题解 五、列主元素消去法 5.1 例题 5.…

python爬虫实战

import requests import json yesinput(输入页数&#xff1a;) yesint(yes)headers {"accept": "application/json, text/plain, */*","accept-language": "zh-CN,zh;q0.9","content-type": "application/json",…

JAVA基础之jsp标准标签

jsp动作标签实现实例化一个实体类 <jsp:useBean id"标识符" class"java类名" scope"作用范围"> 传统的java方式实例化一个实体类 Users user new Users(); <%%> id: 对象名 * class:类 创建对象时,完全限定名(包名…

Linux基础之yum和vim

目录 一、软件包管理器yum 1.1 软件包的概念 1.2 软件包的查看 1.3 软件包的安装和删除 二、Linux编辑器之vim 2.1 vim的基本概念 2.2 正常模式&#xff08;命令模式&#xff09; 2.3 底行模式 2.4 输入模式 2.5 替换模式 2.6 视图模式 2.7 总结 一、软件包管理器yu…

嵌入式Linux学习第四天启动方式学习

嵌入式Linux学习第四天 今天学习I.MX6U 启动方式详解。I.MX6U有多种启动方式&#xff0c;可以从 SD/EMMC、NAND Flash、QSPI Flash等启动。 启动方式选择 BOOT 的处理过程是发生在 I.MX6U 芯片上电以后&#xff0c;芯片会根据 BOOT_MODE[1:0]的设置来选择 BOOT 方式。BOOT_M…

Spring - 9 ( 10000 字 Spring 入门级教程 )

一&#xff1a; MyBatis XML 配置文件 Mybatis 的开发有两种方式&#xff1a; 注解XML 我们已经学习了注解的方式, 接下来我们学习 XML 的方式 MyBatis XML 的方式需要以下两步: 配置数据库连接字符串和 MyBatis写持久层代码 1.1 配置连接字符串和 MyBatis 此步骤需要进…