人人都能学会的编程入门课
胡光
原百度高级算法研发工程师
19410 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 38 讲
开篇词 (1讲)
人人都能学会的编程入门课
15
15
1.0x
00:00/00:00
登录|注册

16 | 数据结构(上):突破基本类型的限制,存储更大的整数

你好,我是胡光,咱们又见面了。
上两节呢,我们讲了素数筛这个算法,并且用素数筛算法演示了程序设计过程中的框架思维。其中提到了欧拉筛法,不知道勤奋的你有没有课后自己去学习一下呢?如果你学习了欧拉筛法以后,你会对我所说的框架思维有更深刻的体会。
在之前的文章中,我们介绍过算法和数据结构的作用。当时我讲到,算法的作用是做数据的计算,并且它对于编程的重要意义,不止是停留在那些叫得上来名字的具体算法上面,而是我们称之的算法思维。
算法思维的具体表现,就是我们处理得到相同信息时,所采用的不同的流程方法。这些方法呢,有好坏高低的比较,而评价的标准,主要就是从时空复杂度方面来考量。由于本专栏主要是教会你掌握编程思维,所以,即使你对时空复杂度不是很了解,也不用担心它会影响你的入门编程学习。你只需要知道,这是我们衡量算法好坏的重要指标即可。
前两篇文章呢,其实更多的就是给大家展示算法思维对于程序设计的重要性,并且,我还要在这里提醒一句,算法的底层是数学,适当的补充数学基础,对于算法的学习是有奇效的。
数据结构和算法,前者负责“表示数据”,后者负责“处理数据”。接下来,我将给你讲讲数据结构的重要性。

今日任务

表示数据到底是什么呢?为什么表示数据很重要?通过今天的 10 分钟任务,你就能明白其中的重要意义。这个任务很简单,就是请你实现一个程序,输出 2 的 1000 次方的结果是多少。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文介绍了大整数表示法在数据结构中的重要性和应用。作者首先强调了算法思维的重要性,并介绍了时空复杂度作为算法好坏的重要指标。随后,作者引出了大整数表示法的概念,通过使用数组存储大整数的方式来解决基本数据类型无法表示大整数的问题。具体讲解了大整数加法的计算过程和代码实现,以及如何突破基本数据类型的限制,求解2的1000次方的值。最后,作者总结了大整数的表示法体现了数据结构对程序设计的作用,而大整数的加法和乘法过程则体现了算法对程序设计的作用。整体而言,本文通过具体的例子和解释,生动地展示了大整数表示法的重要性和实际应用,为读者提供了深入理解数据结构的重要性和解决实际问题的思路。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《人人都能学会的编程入门课》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(10)

  • 最新
  • 精选
  • 罗耀龙@坐忘
    茶艺师学编程 利用老师留下的模块,我多跨了几步 1、课文2的1000次方程序的完全体,计算任何正整数的正整数幂。代码如下: /*大数幂*/ #include <stdio.h> #include <string.h> void print_Big(int x, int n){ int a[100000] = {1,1}; for(int i = 1; i <= n; i++){ for(int j = 1; j <= a[0]; j++)a[j] *= x; for(int j = 1; j <= a[0]; j++){ if(a[j] < 10)continue; if(j == a[0])a[++a[0]] = 0; a[j + 1] += a[j] / 10; a[j] %= 10; } } for(int i = a[0]; i >= 1; --i)printf("%d", a[i]); printf("\n"); } int main(){ int x, n; scanf("%d %d", &x, &n); print_Big(x, n); return 0; } 其中数组a的位数不能再大了,不然就是内存溢出······ 2、大数阶乘,就是计算输入的任意正整数的阶乘。代码如下: /*大数阶乘*/ #include <stdio.h> #include <string.h> void print_factorial(int n){//阶乘的函数 if(n < 0){ printf("error\n");//检查机制 return; } else if(n == 0){ printf("1" ); return; } int a[100000] = {1,1}; for(int i = 2; i <= n; i++){ for(int j = 1; j <= a[0]; j++)a[j] *= i;//乘积的值 for(int j = 1; j <= a[0]; j++){ if(a[j] < 10)continue; if( j == a[0])a[++a[0]] = 0; a[j + 1] += a[j] /10; a[j] %= 10; } } for(int i = a[0]; i >= 1; --i)printf("%d", a[i]);//输出结果 } int main(){ int n; scanf("%d", &n);//输入几的阶乘 print_factorial(n); return 0; }

    作者回复: 非常不错。关于函数内部开大数组溢出的问题,你可以查一下malloc 的用法,或者将大数组开成全局数组,也可以解决这个问题,或者在定义数组的时候,加一个 static 关键字,也成。

    2020-08-02
    3
  • 罗耀龙@坐忘
    茶艺师学编程 1、课文的例子代码是在的太漂亮了 2、课文的“搞事情”,看似很简单,老师也十分亲切地给出了思路,其中的关键就是读入字符串数字,然后处理成整数数组······我找到的方法就是利用字符ACSII码性质来实现,说起来没什么,但就是整整耗了两个星期······· 3、无心插柳,我居然把老师的大数数组加法给模块化了。明明在第一次尝试改造时各种出错······ /*交作业:*/ #include <stdio.h> #include <string.h> void printBIG(char a[], char b[]){//定义函数printBIG,实现输入两个“字符串”大数,相加,输出“数字”大数 int c[1000] = {1,1}; int len_a, len_b; len_a = strlen(a) ; len_b = strlen(b) ; c[0] = ((len_a > len_b) ? len_a : len_b); for(int i = 1; i <= c[0]; i++){//从最小位开始相加,从左往右地放在数组C里 c[i] = (a[len_a - i] - '0') + (b[len_b - i] - '0') ;//这里字符数字换成数字数字的窍门是,字符0-9的ACSII码-48(字符0的ACSII码)=数字0-9 } for(int i = 1; i <= c[0]; i++){//进位处理 if(c[i] < 10)continue;// 每一位是否满10? if(i == c[0])c[++c[0]] = 0;//是否要进位 c[i + 1] += c[i] / 10;//满10进1 c[i] %= 10;//原位除10求余 } for(int i = c[0]; i >= 1; --i)printf("%d", c[i]);//胡老师给的倒序输出语句太漂亮了,我直接拿来用了 printf("\n"); } int main(){ char a[1000], b[1000]; scanf("%s %s", a, b); printBIG(a, b); return 0; }

    作者回复: 哈哈哈,看到你的进步,真的替你开心,加油!d(^_^o)

    2020-07-26
    2
  • 罗耀龙@坐忘
    茶艺师学编程 之前发的代码后来发现有BUG,只要两个数的位数不一致的时候就不能正常运算。 现在修整了该BUG。 其中思路是把两个数的位数情况都遍历:相同、第一个数比第二个数多、第一个数比第二个数少。 以下是修整后的代码: #include <stdio.h> #include <string.h> void printBIG(char a[], char b[]){//定义函数printBIG,实现输入两个“字符串”大数,相加,输出“数字”大数 int c[1000] = {1,1}; int len_a, len_b; len_a = strlen(a) ; len_b = strlen(b) ; c[0] = ((len_a > len_b) ? len_a : len_b); for(int i = 1; i <= c[0]; i++){//从最小位开始相加,从左往右地放在数组C里 if((len_a - i) >= 0 && (len_b - i) >= 0)//位数相同的情况 c[i] = (a[len_a - i] - '0') + (b[len_b - i] - '0') ;//这里字符数字换成数字数字的窍门是,字符0-9的ACSII码-48(字符0的ACSII码)=数字0-9 else if((len_a - i) >= 0 && (len_b - i) < 0)//数1的位数比数2大的情况 c[i] = (a[len_a - i] - '0'); else if((len_a - i) < 0 && (len_b - i) >= 0)//数1的位数比数2小的情况 c[i] = (b[len_b - i] - '0'); } for(int i = 1; i <= c[0]; i++){//进位处理 if(c[i] < 10)continue;// 每一位是否满10? if(i == c[0])c[++c[0]] = 0;//是否要进位 c[i + 1] += c[i] / 10;//满10进1 c[i] %= 10;//原位除10求余 } for(int i = c[0]; i >= 1; --i)printf("%d", c[i]);//胡老师给的倒序输出语句太漂亮了,我直接拿来用了 printf("\n"); } int main(){ char a[1000], b[1000]; scanf("%s %s", a, b); printBIG(a, b); return 0; }

    作者回复: 非常不错!d(^_^o)

    2020-07-30
    1
  • Jinlee
    老师您好,计算2的10次方并输出的示例代码中,我始终没明白为什么 num[0] 会变呢?不是一直都是初始化的值 1 吗?还有, num[j + 1] += num[i] / 10 这一行代码中的 I 是否应该为 j 呢?

    作者回复: 你注意观察 num[j + 1] += num[j] / 10; 这行代码的上一行代码,上一行代码说明,如果发生进位的是 num 大整数的最高位,就给 num[0] + 1,并且给新增的1位,赋为初值 0。 你说的没错,i 应该是 j,笔误。

    2020-03-01
    3
    1
  • 徐洲更
    对于原来的数据类型而言,我们的数组12345,底层使用64位存储的。而为了突破数据类型本身的限制,存放更大的数字,我们新建了一个数组,数组每个元素大小也是64位的话, 也就是说为了表示12345,实际上使用了更多的内存空间。 那么,有没有通过二进制本身,比如说在底层搞一个新的类型,比如说super long, 用128位或者更多位来表示这个大数据类型的方式呢?

    作者回复: 这个是有的。GNU 标准下,是支持 128 位整型数据的。对于你提出的内存浪费的事情,其实文中给出的是最基本的一种实现,真正应用的时候,你可以尝试每一个整型位置存储若干位的数字, 例如:123456--> 12 | 34 | 56

    2020-02-22
    1
  • 🤪HappyJoo
    老师您好,可以麻烦您有空的话帮我解决几个问题吗?谢谢!: 1,在标准C中是 以“__”开头,所以在标准C中要写成“__typeof()”或“__typeof__()”,在GNU C 中还支持直接写“typeof()”,我看您写的都是”__typeof()",其实三个都是一样的,但是否为了不造成不必要的麻烦,才用了标准C的第一个呢? 2,“一起动手搞事情”前面的代码,第23行,“c[i + 1] += c[i] / 10;”,请问这里直接用“+= 1”不行吗?我在这里看了好久才看明白就是一个“进一”的意思(笑哭)。 3,第22行,"c[++c[0]]",这里是不是让“c[0]“先加一再拿来用?用"c[0]++"就会先用原来未加一的数值,然后才加一?我理解的对吗? 谢谢老师,欧拉筛法我看了一些blog,看的头晕,不知道他们的一些变量的作用是什么?例如(https://blog.csdn.net/tianwei0822/article/details/78309453),cnt 是用来干什么的?里面的for循环看不太懂哈哈哈~

    作者回复: 1、是的,之前不注意的时候,踩过坑,后来习惯养成了,就顺手写__typeof了。 2、对于加法来说确实可以,写成%=只是为了不增加你们的学习负担,否则加法学一套,乘法又得学一套。你能发现这其中的问题,说明你是很认真看了的!给你点赞! 3、你理解的是对的。 我在手机上操作的,不方便打开你的链接,我盲猜一下,cnt是用来记录素数个数的。

    2020-02-20
    3
    1
  • 1043
    在计算机中虽然不用自己去计算,但是得理解计算机是怎么计算和存储与表达出来给人类能看懂的逻辑运算。越是高效的算法越简洁,计算机执行效率越高,但是越高效的算法需要数学能力和数学思维的水平也要越高才能做到。在数学思维上和数学在程序语言的表达逻辑上好像总有些差别。

    作者回复: 嗯,对的,这个差别,就是我们所谓的编码能力:将思想转换成代码的能力。(。ì _ í。)

    2020-04-12
  • 一日
    有个疑问:并列的两个for循环:for (int j = 1; j <= num[0]; j++); 虽然在时空复杂度,两个并列的时间复杂度取最高,那这两个循环合并为一个是不是效率也会提高一些,虽然现在计算机执行速度很快了。

    作者回复: 你要怎么合并呢?我看看你的代码?

    2020-03-04
  • look for
    作业: #include <stdio.h> #include <string.h> // 定义交换两个变量值的宏 #define swap(a, b) \ { \ __typeof(a) _t = a; \ a = b; \ b = _t; \ } // 实现大整数加法,a + b的结果,存放在c中 void plus_big_integer(int *a, int *b, int *c) { // 让a指向位数较长的那个数 if (a[0] < b[0]) { swap(a, b); } // 大整数c的位数以a的位数为基准 c[0] = a[0]; // 循环模拟按位加法 for (int i = 1; i <= a[0]; i++) { if (i <= b[0]) { c[i] = a[i] + b[i]; } else { c[i] = a[i]; } } // 处理每一位的进位过程 for (int i = 1; i <= c[0]; i++) { if (c[i] < 10) { continue; } // 判断是不是最高位产生了进位,如果是则进行初始化 if (i == c[0]) { c[++c[0]] = 0; } c[i + 1] += c[i] / 10; c[i] %= 10; } } // 将一个数字字符串转换位大整数表示法 void trans_big_int_expression(char *str, int *arr) { int slen = strlen(str); arr[0] = slen; for (int i = 0; i < slen; i++) { arr[slen - i] = str[i] - 48; } } void main() { int a[20], b[20], c[20] = {0}; char stra[20], strb[20]; scanf("%s%s", stra, strb); // 转换为大整数表示法 trans_big_int_expression(stra, a); trans_big_int_expression(strb, b); // 进行大整数相加,并存入c中 plus_big_integer(a, b, c); for (int i = 0; i < 20; i++) { printf("%d", c[i]); } }
    2022-10-20归属地:山东
  • 武汉李先生
    胡船长好,这行代码中 if (j == num[0]) num[++num[0]] = 0; 赋值为0的操作是不是可以省略,num数组在声明的时候已经初始元素全为0,还是说在有的情况下不是0,为了安全起见这样做的。
    2020-10-04
收起评论
显示
设置
留言
10
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部