有一次在牛客网上做北邮考研复试的一道题,题面如下:
(声明:本人在查看部分已通过代码和自己提交的代码后得知在牛客网中提交此题只需要做到把一个十进制数转化成二进制数,如果按题目要求来做估计就是错误,若有疑问请在评论区说明)
原题链接:二进制数
第一次写博文,写的不好就原宥一下啦~
二进制数
大家都知道,数据在计算机里中存储是以二进制的形式存储的。 有一天,小明学了C语言之后,他想知道一个类型为unsigned int 类型的数字,存储在计算机中的二进制串是什么样子的。 你能帮帮小明吗?并且,小明不想要二进制串中前面的没有意义的0串,即要去掉前导0。
时间限制:C/C++ 1秒,其他语言2秒空间限制:C/C++ 64M,其他语言128M
样例测试
输入
5
23
535
2624
56275
989835
输出
10111
1000010111
101001000000
1101101111010011
11110001101010001011
看上去很简单对不对?
其实这个题认真做应该是涉及动态内存分配的,因为题目并没有给出n的范围。但是想骗过样例你开个大点的数组就行了
十进制转二进制的常规做法
这个应该是刚学C++的时候做的一个练习题,不详细赘述了,如果是初学者请看代码的注释。对其中的数学原理有疑问的建议看看 百度百科:十进制转二进制
#include<iostream>
#include<string>
using namespace std;
//如果不输入using namespace std,那么在使用cin,cout,string前面都要加上一个std::以表示命名空间
int main(){
string binary = "";//存在字符串中,最后逆向输出即为答案
int num; cin >> num;
do {
switch (num % 2) {
case 0:binary = binary + "0"; break;//余2为0,向binary中存入字符0
case 1:binary = binary + "1"; break;//余2为1,向binary中存入字符1
default:continue;//防止电脑脑抽连num%2都算错,不过可能性很小可以不写
}
num /= 2;//取num/2作为下一次循环的num
} while (num != 0);//这样写循环就是先做了do里面的再来看循环条件以判断要不要进入下一循环
for (int i = binary.size() - 1; i >= 0; --i)
cout << binary[i];//逆向输出
return 0;
}
也可以稍微复杂一点,利用递归来做也可以
#include<iostream>
using namespace std;
void binary(unsigned int n){
if(n==0)return;//n=0时必须结束不然就是个“死循环”,系统不断调用binary(0)
//而且这里如果不结束的话会导致结果错误
binary(n/2);//要得到正序的结果就先把n/2算到底,返回来取n%2
if(n%2==0)cout<<"0";
if(n%2==1)cout<<"1";
}
int main(){
unsigned int n;
cin>>n;
if(n==0)cout<<"0";//n=0时特殊判断一下
else binary(n);
return 0;
}
以上方法时间复杂度都是O(logn)
我遇到的问题
因为考虑到动态内存分配,第一次就想到使用vector来做,但是结果竟然是超空间(我觉得应该是题目的问题,开头已讲)。后面查了一下,使用new或者stack、list效果会好一些。代码如下,有需要借鉴的可以看一下
#include<iostream>
#include<string>
#include<vector>
using namespace std;
int main(){
vector<unsigned int>number;
int num;cin>>num;int n;
vector<unsigned int>::iterator iter1=number.end();
for(int i=0;i<num;++i){
cin>>n;
number.insert(iter1,n);
iter1=number.end();
}//这上面相当于利用vector实现动态数组的方法,可以背下来
vector<string>bin;
vector<string>::iterator iter2=bin.end();
for(int i=0;i<num;++i){
bin.insert(iter2,"");
iter2=bin.end();
}
for(int i=0;i<num;++i){
do{
if(number[i]%2==0)bin[i]=bin[i]+"0";
if(number[i]%2==1)bin[i]=bin[i]+"1";
number[i]/=2;
}while(number[i]!=0);
}
for(int i=0;i<num;++i){
for(int j=bin[i].size()-1;j>=0;--j)//还是一样逆序输出
cout<<bin[i][j];//bin[i]是bin中索引为i的字符串,j是该字符串的一个索引
cout<<endl;
}
return 0;
}
不过我遇到了一个很奇怪的问题,这一段代码在我电脑上的vs跑不起来,调试时遇上了无法输入的问题。
经测换DEV来跑就可以了
后面问了一下大佬们,遇到这种看上去像动态数组的题你做一个输出一个就完事了
位运算
在开始正式节目之前,咱们先来看看位运算是什么东西(如果你已经了解了相关概念可以跳过这部分)
位运算的概念基本上在大学计算机、软件相关专业的数字逻辑、计算机组成等课程中学到
先假设int a=1,int b=2
符号 | 含义 | 举例 |
---|---|---|
<< | 算术左移 | a<<1后a=2(1->10) |
>> | 算术右移 | a>>1后a=0(1->0) |
& | 按位与 | a&b=01&10=00 |
l | 按位或 | a l b=01 l 10=11 |
(表格中最后的或符号用的是字母l,我不知道怎么在表格里打单杠。。。)
计算机中存储数字都是二进制形式,只是需要你把它打印出来而已(所以java和python中直接有对应的转化函数)
想把一个十进制数字转化为二进制,按位来与就可以
如:2&1:10&01取第一位0,1左移成10,10&10取第2位1
所以十进制数字2转化为二进制为“10”
解决问题
题目中输入的参数类型是unsigned int,总共有32位,也就是说最多也只是要做32次位运算就可以得到结果
但是问题还是有的:1.逆向输出;2.去除多余的0
思路:1.要做到逆向输出,可以上string,下面示例代码的方法建议也看一下,对日后理解数据结构也不错
2.去除多余的0:之所以会有多余的0,是输入的数字在做完某一次位运算后,再做得到的也只是0。所以只要在每一次位运算后都要原来的数字除以2,到0停止就可以了
代码如下:
#include <iostream>
#include <string>
using namespace std;
class stack {
public:
int max_size;
int current_size;
unsigned int *p;
//Constructor & Destructor
stack(){
current_size = max_size = 0;
p = nullptr;
}
stack(unsigned int n) {
p = new unsigned int[n];
max_size = n;
current_size = 0;
}
~stack() { delete[] p; }
void add(unsigned int num) {
if (current_size == max_size)return;
else {
for (int i = current_size - 1; i >= 0; --i) {
p[i + 1] = p[i];
}
p[0] = num;
++current_size;
return;
}
}
void print() {
for (int i = 0; i < current_size; ++i)
cout << p[i];
cout << endl;
}
};
int main() {
int m; cin >> m;
unsigned int n;
//位运算
while (m>0) {
int b = 1;
cin >> n;
if (n == 0)cout << 0 << endl;
int k = n;//记录输入的数字,因为后面会有n/=2
stack s(32);//因为最多是32位,就让max_size为32了
for (int i = 0; i < 32 && n!=0; ++i) {
if (k & b) s.add(static_cast<unsigned int>(1));
else s.add(static_cast<unsigned int>(0));
b = b << 1;
n /= 2;
}
s.print();
m--;
}
return 0;
}
如果要在VS上试运行以上代码,记得加上可能所需要的头文件,如“pch.h”等
希望对大家学习有所帮助,谢谢:)