前言
进制转换的问题,在算法与数据结构中是一个比较基础的问题,大多数公司都不会对其进行考察,但是如果考察就是十分致命性的题目,因为大多数人对这块基本的逻辑是不明白的,甚至对于在相应的语言中如何表示不同的进制数都不清楚。
二进制可以使用的数字:0,1
八进制可以使用的数字:0,1,2,3,4,5,6,7
十进制可以使用的数字:0,1,2,3,4,5,6,7,8,9
十六进制可以使用的数字:0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F
java中能够用的数值数据类型:java为了有效的实现跨平台性,在java中只提供了byte、short、int、long、float、double等数值类型,不同于c、c++中提供了无符号数值类型,unsigned byte,unsigned int,java中没有,所以在一些进制转换中不太好做。
java取模运算中,余数的符号和被除数符号相同(除号前面的数),即与第一个数的符号相同
不同进制数在Java中的表示
不仅仅是Java中,大多数语言中都这样声明不同的进制数
二进制前缀:0b
八进制前缀:0
十六进制前最:0x
//都表示二进制10
int a=0b1010;
int a=012;
int a=0xA;
java中不同进制间的输出
String b=Integer.toBinaryString(a);
String c=Integer.toOctalString(a);
String d=Integer.toHexString(a);
不同进制数间的转换
题目1:负二进制转换
class Solution {
public String baseNeg2(int n) {
//普通的10进制数转其他进制数的做法
int k=-2;
StringBuilder sb=new StringBuilder();
if(n==0)
{
return "0";
}
while(n!=0)
{
int d=n%(k);
if(d<0)
{
d=(d+Math.abs(k))%Math.abs(k);
}
sb.append(d);
n=(n-d)/k;
}
return sb.reverse().toString();
}
}
主要需要解决的问题是余数为负数的情况,当多次迭代做商之后,被除数为负,所以出现余数为负的情况,这时候需要求其正的同余数{d+Math.abs(k))%Math.abs(k)}。
这道题还有一个难点我们一般做正数迭代求余操作的时候,直接n=n/k
即可,因为除法是向下保留的{例如,9/2=4,而不是等于5}所以不会出现“缺失”的问题,但是对于负数而言,{9/(-2)=-4,-1/-2=0}是向上取,所以需要我们减去同余数,使其向下取。
这道题可以提炼出任何正数转化为K进制(K可以为任意正数或负数)的代码。
String s="0123456789abcdef"
StringBuilder res=new StringBuilder;
char []alphabet=s.tocharArray();
while(n!=0)
{
b=n%k;
if(b<0)
{
b=(b+Math.abs(b))%Math.abs(b);
}
res.append(alphabet[b]);
n=(n-b)/k;
}
对于一般的做法讲完之后,对于这道题,由于是-2,所以还可以用另外的情况去做,这直接避免了不必要的麻烦讨论
public String baseNeg2(int N) {
StringBuilder res = new StringBuilder();
while (N != 0) {
res.append(N & 1);
N = -(N >> 1);
}
return res.length() > 0 ? res.reverse().toString() : "0";
}
上面这道题目可以变化的更难一点,因为这个题目中,并没有考虑N<0的情况
题目2:数字转换为十六进制数
这道题其实是最基本的题目,没有任何复杂度,唯一需要考虑的就是负数时的额外处理。而这个处理会引出很多讨论,纵使其一句代码可以解决。
首先贴上解决代码
public String toHex(int num)
{
if(num==0)
{
return "0";
}
long lnum=num;
if(num<0)
{
lnum=Math.pow(2,32)+num;
}
while(lnum!=0)
{
long b=lnum%16;
char c=(char)(b+'0');
if(b>10)
{
c=(char)(b-10+'a');
}
res.append(c);
lnum=lnum/16;
}
return res.reverse().toString();
}
引出的讨论如下:
计算机在存储负数的时候本身用的就是补码,负数在其内部的存储我们就是我们最终想要的结果,奈何这些语言的发明者替我们考虑了太多。
32位有符号整型的范围的分析,32个bit,第一个bit是用来表示符号位的,那么最大的正数为:0111-1111-1111-1111-1111-1111-1111-1111
,2147483647
最小的数是多少呢?如果没有什么补码,原码、反码之说,那么我们可以想到的最小的数1111-1111-1111-1111-1111-1111-1111-1111
,-2147483647
其实不对!!!
对于+0的原码为0000-0000-0000-0000-0000-0000-0000-0000
,
-0的原码为1000-0000-0000-0000-0000-0000-0000-0000
,
因为0只需要一个,所以把-0拿来当做一个最小的数-2147483648
。
-2147483648
的补码表示为1000-0000-0000-0000-0000-0000-0000 -0000
,在32位没有原码。
注意,这个补码并不是真正的补码,真正的补码是1 1000 0000 0000 0000 0000 0000 0000 0000,溢出。
先枚举几个负数的补码值: -1对应0xFFFFFFFF,(看作0xFFFFFFFF+1-1);-2对应0xFFFFFFFE,(看作0xFFFFFFFF+1-2)。我们可以发现,对于一个给定的负数,只需要将其加上0xFFFFFFFF+1,再减去该负数的绝对值即可得到该负数对应的补码值。