- 问题描述
Description
将一个很长的十进制数转换为二进制数
注意由于输入的十进制数可能很长,超过了C语言int、long long 、double等基本数据类型的存储范围,需要自行用字符数组等方式存储输入,并自行编写除法等进制转换必须的相关操作
Input
输入多组数据,请处理到文件结束。每组数据一行,是一个很长的十进制数,长度大于0,小于200
Output
对于每组数据将其转换为二进制
Sample Input 1
123456789012345678901234567890 753951684269875454652589568545854758545824
Sample Output 1
1100011101110100100001111111101101100001101110011111000001110111001001110001111110000101011010010 10001010011110101010001101001000100100100000101001011010001010101001000100111101011001110001000111110010001000101101111110110110100110100000
注:题目来源OnlineJudge
- 问题分析
题目要求转换一个很长的十进制数字,用int、long、long long、double显然是存不下的,所以这里创建一个char类型的数组接收数据,再进行处理。但是以字符串形式接收数据并存放到char类型的数组中后该数据的每一位数都被独立开了,即输入147时实际是得到字符1、4、7。那我们如何去处理接收到的数据呢?
我们知道进制转换,其核心就是除法的实现。所以下面先补充一个除法的实现过程,帮助大家理解除法的实现。
除法实现过程
//除法的实现
//举一个简单的例子,如用125除以2
//其实现过程如下:
//1.从最高位开始除,得到当前位的商和余数
// 1/2=0,1%2=1
// 所以这一次的结果是商0余1
//2.从次高位数开始,将上一位数除2后所得余数乘以10加上当前位置的数作为新的被除数,再进行除2操作,得当前位的商和余数
// 新的被除数是1*10+2=12
// 12/2=6,12%2=0
//3.继续取下一位数,将上一位数除2后所得余数乘以10加上当前位置的数作为新的被除数,再进行除2操作,得当前位的商和余数
// 新的被除数是0*10+5=5
// 5/2=2,5%2=1
//4.重复上面3中的步骤,直到新的被除数为0,此时余数为整个除法的最终余数,整个除法结束,依次记录每一步骤的商可得到最终的商
// 最终的商是062,即62
// 最终的余数是1
从上面的例子中,如果要将十进制数125转换成对应二进制数,我们只需按照上述方法处理每一轮除法,每一轮除法结束取最终余数和商,如果商不为0,去上一次的商作为新的被除数处理,直到商为0,余数依次按顺序存下,最后逆序即可得到对应二进制数。
进制转换实现过程
//二进制转换实现
//以125转对应二进制数为例
//1.第一轮除法,得到如下结果
// 最终的商是062,即62
// 最终的余数是1
//2.上一轮除法最终得到的商不为0,继续以上一轮的商作为新的被除数进行新一轮除法,即以62作为新的被除数,得到如下结果
// 最终的商是31
// 最终的余数是0
//3.继续2中的步骤,直到最终的商为0,整个转换结束,如果按顺序依次存放每一轮除法所得最终余数,那么我们逆序即所求对应二进制数
//第一轮,125作为被除数
// 最终的商:062
// 最终的余数:1
//第二轮,062作为被除数
// 最终的商:031
// 最终的余数:0
//第三轮,031作为被除数
// 最终的商:015
// 最终的余数:1
//第四轮,015作为被除数
// 最终的商:007
// 最终的余数:1
//第五轮,007作为被除数
// 最终的商:003
// 最终的余数:1
//第六轮,003作为被除数
// 最终的商:001
// 最终的余数:1
//第七轮,001作为被除数
// 最终的商:000
// 最终的余数:1
//第七轮中最终的商为0,整个转换过程结束
// 按顺序存放每一轮的最终余数:1011111
// 逆序余数可得所求二进制数:1111101
从上面的例子中,我们可以理解除法和进制转换的关系了,进制转换就是多次除法,每一次的被除数都是上一次除法所得的商。
以此为基础,考虑本题中的代码实现过程
1.将char类型数组中的char类型数据转换成int类型数据并存放在int类型数组中
除法中除的过程和取模的过程处理对象都是int类型,所以我们先将接收到的字符串数据转换成对应的int类型的数据并存放在一个int类型的数组中。
char类型数字转换成对应int类型数字,可用:int类型数字=char类型数字-'0'。原理其实也很简单,char接收的数据在内存中存放是其对应的ASCll值,所以这里用char类型数字减去字符0,差值即对应int类型的数字。我们写一个transform_int函数,函数实现代码如下:
char num_char[200] = { 0 };//用于存放输入的超长十进制数
int num_int[200] = { 0 };//用于存放转换成整型数据的超长十进制数存放
void transform_int(char num_char[], int num_int[])
{
int sz = strlen(num_char);
int i = 0;
for (i = 0; i < sz; i++)
{
//按顺序存入输入的数据,num_char[0]是最高位
num_int[i] = num_char[i] - '0';//数字(字符类型)转换成对应数字(整数类型)
}
}
2.在进行每一轮的除法前,判断当前的商是否为0
数据是以int类型数组存放,如果数组中元素全为0,那么当前转换结束,不进入下一次除法运算。即只需遍历该数组,若存在一个非0的数,则数据未处理结束,进行下一轮除法。因为进行下一次除法前都要进行判断,我们可以将判断的功能独立出来,分装成一个函数,每次判断时调用即可。这一部分的代码我们也写成一个函数is_allzero,函数实现代码如下:
int is_allzero(int num_int[], int sz)
{
//判断是否存在非零的数
int i = 0;
for (i = 0; i < sz; i++)
{
if ((num_int[i])!= 0)//写成if ((num_int[i])^ 0)也可,运用了异或运算符
{
return 1;//返回1表示仍存在非0的数
}
}
return 0;//返回0表示数据全部处理为0
}
3.进制转换的实现,处理到最终数据为全零0,进制转换的核心
基于上面给大家补充的除法实现过程,和进制转换实现过程,如果可以深刻的理解除法实现过程和进制转换实现过程,那么我们将其用一个函数transform_binary表示出来,其实现过程如下:
void transform_binary(int num_int[], int binary[], int sz)
{
int i = 0;
int count = 0;//计数器,用于计算结果的位数
while (is_allzero(num_int, sz))//在开始每一轮大数除法前进行一次判断,处理到数据全为0时结束
{
//大数除法核心
//下面进行一轮大数除法
int temp_before = 0;//用于存放一轮大数除法结束后的余数,开始每一轮大数除法前,上一次余数置为0
for (i = 0; i < sz; i++)
{
int temp = num_int[i] + temp_before * 10;//每一次的被除数=当前位置的数+上一次余数*10
num_int[i] = temp / 2;//用每一次除得的商替换上一次的数据
temp_before = temp % 2;//每一次取模值(余数)留给下一次处理
}
binary[count++] = temp_before;//每一轮大数除法结束后所得最终余数按顺序存放进binary数组
}
//逆序输出binary
for (i = count-1; i >= 0; i--)//while循环结束时count多自增了1,binary中最后一个元素的下标为count-1
{
printf("%d", binary[i]);
}
}
下面对transform_binary函数实现过程进行补充,帮助大家理解。
在进入while循环前,我们先进行一次判断,判断当前要处理的数据是否仍存在非0的数,若存在,则进入循环。
在上面的除法实现过程中,我们知道每一轮除法会产生一个余数,且下一次除法会进行使用,这里创建一个int类型变量temp_before,该变量用于存放一轮除法中每一次除法的余数,在该轮中一下除法时利用。另外创建一个int类型临时变量temp用于存放当前的被除数。所以进入下一次除法时,当前被除数=当前位+temp_before*10,即int temp = num_int[i] + temp_before * 10,本次除法余数又重新存放到temp_before中,即temp_before = temp % 2,同时将本次除法得到的商存放回当前位数,即num_int[i] = temp / 2,继续进行下一次除法,直到本轮除法结束,每一次除法的商都依次存放回原数组,覆盖上一次要处理的数据,最后的temp_before即本轮除法最终的余数,将每一轮的最终余数按顺序存放进binary数组中。
下一轮以上一轮覆盖原数组后的数据在while中重复上述过程,处理到num_int数组中数据全部为0时结束进制转换过程,最后就是逆序输出binary数组中的数据,即对应二进制数。
- 解决方案
通过上面的整个分析过程,AC代码如下:
#pragma warning(disable:4996)
#pragma warning(disable:6031)
#pragma warning(disable:6054)
#pragma warning(disable:6385)
#include<stdio.h>
#include<string.h>
void transform_int(char num_char[], int num_int[]);//将字符类型的数据转换成整数类型的数据
void transform_binary(int num_int[], int binary[], int sz);//将输入的十进制数转换成二进制数
int is_allzero(int num_int[], int sz);//判断需处理的数据是否为全0
int main()
{
//创建的数组大小根据题目而定
char num_char[200] = { 0 };//用于存放输入的超长十进制数
int num_int[200] = { 0 };//用于存放转换成整型数据的超长十进制数存放
int binary[1000] = { 0 };//用于存放二进制数据结果
while (scanf("%s", num_char) != EOF)//按顺序存入输入的数据,num_char[0]是最高位
{
int sz = strlen(num_char);
transform_int(num_char, num_int);
if (is_allzero(num_int, sz) == 0)//处理前先进行一次判断,若输入的数据是全0,则输出0
{
printf("0");
}
else
{
transform_binary(num_int, binary, sz);
}
printf("\n");
}
return 0;
}
void transform_int(char num_char[], int num_int[])
{
int sz = strlen(num_char);
int i = 0;
for (i = 0; i < sz; i++)
{
//按顺序存入输入的数据,num_char[0]是最高位
num_int[i] = num_char[i] - '0';//数字(字符类型)转换成对应数字(整数类型)
}
}
void transform_binary(int num_int[], int binary[], int sz)
{
int i = 0;
int count = 0;//计数器,用于计算结果的位数
while (is_allzero(num_int, sz))//在开始每一轮大数除法前进行一次判断,处理到数据全为0时结束
{
//大数除法核心
//下面进行一轮大数除法
int temp_before = 0;//用于存放一轮大数除法结束后的余数,开始每一轮大数除法前,上一次余数置为0
for (i = 0; i < sz; i++)
{
int temp = num_int[i] + temp_before * 10;//每一次的被除数=当前位置的数+上一次余数*10
num_int[i] = temp / 2;//用每一次除得的商替换上一次的数据
temp_before = temp % 2;//每一次取模值(余数)留给下一次处理
}
binary[count++] = temp_before;//每一轮大数除法结束后所得最终余数按顺序存放进binary数组
}
//逆序输出binary
for (i = count-1; i >= 0; i--)//while循环结束时count多自增了1,binary中最后一个元素的下标为count-1
{
printf("%d", binary[i]);
}
}
int is_allzero(int num_int[], int sz)
{
//判断是否存在非零的数
int i = 0;
for (i = 0; i < sz; i++)
{
if ((num_int[i])!= 0)//写成if ((num_int[i])^ 0)也可,运用了异或运算符
{
return 1;//返回1表示仍存在非0的数
}
}
return 0;//返回0表示数据全部处理为0
}
- 总结
本题的难点在于题目要求处理的是超长的数据,我们只能用字符串接收存放于char类型数组中,其次要深刻理解除法、进制转换及他们之间的关系,最后用代码把他们表达出来。这种超长数据进制转换处思路,在其他进制转换中也可以使用,如果读者能够理解掌握上面的思路,在遇到类似题目时,相信大家可以如行云流水般写出AC代码。
以本题为基础,实现k(1<k<=10)进制其实是用一样的套路将上述操作数2改为k即可
- blog心路历程
我 21年9月入学,此前并没有接触过编程类相关知识,那时的我对C语言入门真的毫无头绪,找学姐学长、还有预科的兄弟请教如何入门C,你们对我的帮助真的很多,在这里先表示感谢。
我至今仍记得第一次在小黑窗打印出hello world时的兴奋,这是我模仿课本《C语言程序设计》(第四版何钦铭)打出的第一个代码,在那之后,我跟着课本在dev++打出例题的代码,编译运行,每一次运行成功,小黑窗显示出期待的内容真的是一件令人鼓舞的事情。在简单地跟着课本例题过了一遍C语言大部分知识点后,通过中国大学MOOC、B站、网易公开课等平台继续对知识进行补充。
当然在学习C语言过程中,测试和期考是必不可少的过程,在第一次上机作业的发布,那时的刚入门C语言,对于解决这种Online Judge的题目还是毫无头绪的,因此一次作业、二次作业并不理想,当时的我还在入门C阶段。到第三次作业发布时,那时已成功入门C,已经能自己解决问题、找解题思路、调试、提交前的数据自测,应该说C语言相关知识我已经掌握了大部分了,基本能解决一些问题,看得懂别人的代码。
11月14日晚上18:30西综合,期中考试如约而至,确实如老师所说,六题中四题出自平时作业题库,另外两题是新题。而我只做出最基本的三道题,那两道新题,此前未接触过,做不出那就不说了,但是有一题考前我是做过的,就是这篇blog中的题目,超长十进制转换二进制。虽然考前作业做过,但是考试时,我却完全做不出来,失分于这种已经做过的题目真的是耻辱。在考试结束当天晚上,我自己也进行了思考:我真的学会C语言了吗?显然,我接触到的C语言只是皮毛,关于C语言还有很多知识需要去学习并彻底掌握,所以我打算再详细地过一遍C语言,总结知识点,通过网课等途径继续学习C语言相关知识。打好基础真的很重要,总不能等这个学期结课了,C语言还没掌握。同时,我也计划通过重新做OJ题并继续解决问题的总结以加深理解。结合学习C语言和做OJ题的过程,根据实际情况和时间安排,可以写一些blog。
这就是我写这篇blog的历程,开学这么久,这其实是我的第一篇blog,也是对我学习过程的一个阶段性总结。坚持做有意义的事,总会有收获。