面试题-求一个数的二进制数中1的个数
2014-04-24 21:58
316 查看
某大型社交互联网公司暑期实习生一面有个面试官出了这么一道题目,求一个数的二进制数中1的个数,如何求呢。
我的想法是这样的,对于一个int类型的正数,占了4字节共32位,平时把这个数转成二进制数我是这样计算的,假设这个数为num,先找一个2的n次方的数,这个数刚刚好比待解的数大,然后2的n-1次方比这个数小,即2^n > num > 2^(n-1),那么我们就可以知道在num = 2^(n-1)+rest
,然后在剩下的部分用同样的方式,知道rest = 0。即用这种方式把这个数num分解成不同的2的幂的组合。
举个例子,7这个数,先找到2^3 = 8 ,比7大,即 2^3>7>2^2,所以 7 = 2^2 + 3 ,对于3 用同样的方式进行判断,先判断3是否大于2^(2 - 1)= 2^1,所以有 2^2>3>2^1,这个时候3 = 2^1+1,再判断1, 2^1>1>=2^0 , 所以1 = 1^1 +
0,此时rest = 0,因此 7 = 2^2+2^1+1 。即 7 的二进制数为 111,因此在程序中,我们可以维持一个32个元素的boolean数组,相应的位置置为true,其他位置默认都是false,所以在7这个数中,数组的第0喂和第1位、第2位都是true,其他都是false。这样就可以得到7的二进制数组。程序用递归比较简单,但是效率不高,所以考虑用while循环替代,在输出的时候是输出一个字符串,由于打印需要多次拼接字符串,所以用StringBuffer这个类来拼接,效率比较高。
程序如下:
运行结果如图:
当然还有一种情况是如果int类型的数可能是正数也可能是负数,要是个负数怎么办呢,我们先来看看负数在计算机里面是如何表示的,定义是这样的:
---------------------------------------------------------------------分割线----------------------------------------------------------------------------
引用自:http://wenwen.sogou.com/z/q127021346.htm
在2进制中,负数是以它正值的补码形式表达
具体来分析一下:
原码:一个整数,按照绝对值大小转换成的二进制数,称为原码。
比如 0000 0000 0000 0000 0000 0000 0000 0101是 5的 原码。
反码:将二进制数按位取反,所得的新二进制数称为原二进制数的反码。
取反操作指:原为1,得0;原为0,得1。(1变0; 0变1)
比如:将5的二进制表达式的每一位取反,得
1111 1111 1111 1111 1111 1111 1111 1010
称:1111 1111 1111 1111 1111 1111 1111 1010 是 0000 0000 0000 0000 0000 0000 0000 0101 的反码。
反码是相互的,所以也可称:
1111 1111 1111 1111 1111 1111 1111 1010 和 0000 0000 0000 0000 0000 0000 0000 0101 互为反码。
补码:反码加1称为补码。
也就是说,要得到一个数的补码,先得到反码,然后将反码加上1,所得数称为补码。
那么,5的补码为:
1111 1111 1111 1111 1111 1111 1111 1010 + 1 =
1111 1111 1111 1111 1111 1111 1111 1011
所以,-5 在计算机中的二进制表达为:
1111 1111 1111 1111 1111 1111 1111 1011
转换为十六进制:0xFFFFFFFB。
---------------------------------------------------------------------分割线----------------------------------------------------------------------------
所以其实很好解决,求一个负数在计算机中的二进制编码,我们只要求它的绝对值的数的反码再加上1,反码很好求,在我们可以求这个数的正数的二进制数组,我们只要按位取反就可以了,就到到反码的数组,然后还要加1,如何加1,其实可以在取反的时候同时进行,就是设置一个是否进位的标志位,初始化为true,从第一个开始判断,首先第一位是有进位的,因为要加1,这里有两种情况,第一种是如果第一位为0
,那么取反后为1,然后接受进位加1,则变成0,然后再向前进位;第二种情况,是第一位为1,取反后为0,接受进位加1后为1,那么不需要进位了,则进位标志位false,然后第二位则根据进位标志为false知道不需要进位了。综合这两种情况,在有进位标志的情况下,其实相当于两次取反抵消,还是原来的值,所以直接判断原来的值是否为0,是则继续进位,否则不需要进位,具体代码如下(虚线内为在前一个版本中改动的):
自己的想法就是这样实现,暂时没有想到更简单的方法,如果有更巧妙简单的方法或者有什么错误的地方,请大家多多指导~~转载请注明出处。~
我的想法是这样的,对于一个int类型的正数,占了4字节共32位,平时把这个数转成二进制数我是这样计算的,假设这个数为num,先找一个2的n次方的数,这个数刚刚好比待解的数大,然后2的n-1次方比这个数小,即2^n > num > 2^(n-1),那么我们就可以知道在num = 2^(n-1)+rest
,然后在剩下的部分用同样的方式,知道rest = 0。即用这种方式把这个数num分解成不同的2的幂的组合。
举个例子,7这个数,先找到2^3 = 8 ,比7大,即 2^3>7>2^2,所以 7 = 2^2 + 3 ,对于3 用同样的方式进行判断,先判断3是否大于2^(2 - 1)= 2^1,所以有 2^2>3>2^1,这个时候3 = 2^1+1,再判断1, 2^1>1>=2^0 , 所以1 = 1^1 +
0,此时rest = 0,因此 7 = 2^2+2^1+1 。即 7 的二进制数为 111,因此在程序中,我们可以维持一个32个元素的boolean数组,相应的位置置为true,其他位置默认都是false,所以在7这个数中,数组的第0喂和第1位、第2位都是true,其他都是false。这样就可以得到7的二进制数组。程序用递归比较简单,但是效率不高,所以考虑用while循环替代,在输出的时候是输出一个字符串,由于打印需要多次拼接字符串,所以用StringBuffer这个类来拼接,效率比较高。
程序如下:
/* 程序来源:面试题 * 源文件名称:NumToBin.java * 要 点: * 输入一个数,输出它二进制中1的个数 */ public class NumToBin{ public static void main(String[] args){ System.out.println(numToBin(65531)); } public static String numToBin(int num){ int c_num = num; //拷贝副本 int a = 1; //初始为2的0次方1 int pow = 0; //2的pow次方 boolean bools[] = new boolean[32]; //数组变量存储1的位置 StringBuffer s = new StringBuffer(""); //由于要循环拼接字符串使用缓冲字符串类效率高 int flag = 0; //存储1的个数 //从2的0次方开始找,找到刚好2^pow > num > 2^(pow-1)的数a ,这里^表示幂,即2的pow次方 while(num>a){ pow++; a*=2; } //这里开始由a往后找,每次找到一个小于等于num的数, //则num减去这个数,然后对应二进制数组中位置置1 //然后继续找直到num为0 while(num>0){ if(num < a){ a/=2; pow--; }else{ num = num - a; bools[pow] = true; } } //找到num的二进制数组中的第一个为1的位置 int index = -1; for(int i = 31;i>=0;i--){ if(bools[i] == true){ index = i; break; } } //循环拼接二进制字符串 if(index>=0) for(int i = index;i>=0;i--){ if(bools[i] == true){ s.append(1); flag++; }else{ s.append(0); } } if(c_num == 0) return c_num + "\t二进制为:" + 0000 + "\t总共 :"+ flag +"\t个1"; else return c_num + "\t二进制为:" + s.toString()+ "\t总共 :"+ flag +"\t个1"; } }
运行结果如图:
当然还有一种情况是如果int类型的数可能是正数也可能是负数,要是个负数怎么办呢,我们先来看看负数在计算机里面是如何表示的,定义是这样的:
---------------------------------------------------------------------分割线----------------------------------------------------------------------------
引用自:http://wenwen.sogou.com/z/q127021346.htm
在2进制中,负数是以它正值的补码形式表达
具体来分析一下:
原码:一个整数,按照绝对值大小转换成的二进制数,称为原码。
比如 0000 0000 0000 0000 0000 0000 0000 0101是 5的 原码。
反码:将二进制数按位取反,所得的新二进制数称为原二进制数的反码。
取反操作指:原为1,得0;原为0,得1。(1变0; 0变1)
比如:将5的二进制表达式的每一位取反,得
1111 1111 1111 1111 1111 1111 1111 1010
称:1111 1111 1111 1111 1111 1111 1111 1010 是 0000 0000 0000 0000 0000 0000 0000 0101 的反码。
反码是相互的,所以也可称:
1111 1111 1111 1111 1111 1111 1111 1010 和 0000 0000 0000 0000 0000 0000 0000 0101 互为反码。
补码:反码加1称为补码。
也就是说,要得到一个数的补码,先得到反码,然后将反码加上1,所得数称为补码。
那么,5的补码为:
1111 1111 1111 1111 1111 1111 1111 1010 + 1 =
1111 1111 1111 1111 1111 1111 1111 1011
所以,-5 在计算机中的二进制表达为:
1111 1111 1111 1111 1111 1111 1111 1011
转换为十六进制:0xFFFFFFFB。
---------------------------------------------------------------------分割线----------------------------------------------------------------------------
所以其实很好解决,求一个负数在计算机中的二进制编码,我们只要求它的绝对值的数的反码再加上1,反码很好求,在我们可以求这个数的正数的二进制数组,我们只要按位取反就可以了,就到到反码的数组,然后还要加1,如何加1,其实可以在取反的时候同时进行,就是设置一个是否进位的标志位,初始化为true,从第一个开始判断,首先第一位是有进位的,因为要加1,这里有两种情况,第一种是如果第一位为0
,那么取反后为1,然后接受进位加1,则变成0,然后再向前进位;第二种情况,是第一位为1,取反后为0,接受进位加1后为1,那么不需要进位了,则进位标志位false,然后第二位则根据进位标志为false知道不需要进位了。综合这两种情况,在有进位标志的情况下,其实相当于两次取反抵消,还是原来的值,所以直接判断原来的值是否为0,是则继续进位,否则不需要进位,具体代码如下(虚线内为在前一个版本中改动的):
/* 程序来源:腾讯面试题 * 源文件名称:NumToBin2.java * 要 点: * 输入一个数,输出它二进制中1的个数,包括负数版本 */ public class NumToBin2{ public static void main(String[] args){ System.out.println(numToBin(-1)); System.out.println(numToBin(15)); System.out.println(numToBin(-15)); } public static String numToBin(int num){ //----------------------------- int c_num; if(num<0) { c_num = num; //拷贝副本 num = num*(-1); //num小于0则取绝对值 }else c_num = num; //拷贝副本 //----------------------------- int a = 1; //初始为2的0次方1 int pow = 0; //2的pow次方 boolean bools[] = new boolean[32]; //数组变量存储1的位置 StringBuffer s = new StringBuffer(""); //由于要循环拼接字符串使用缓冲字符串类效率高 int flag = 0; //存储1的个数 //从2的0次方开始找,找到刚好2^pow > num > 2^(pow-1)的数a ,这里^表示幂,即2的pow次方 while(num>a){ pow++; a*=2; } //这里开始由a往后找,每次找到一个小于等于num的数, //则num减去这个数,然后对应二进制数组中位置置1 //然后继续找直到num为0 while(num>0){ if(num < a){ a/=2; pow--; }else{ num = num - a; bools[pow] = true; } } //----------------------------- //这里如果要转化的数是负数,那么要求其补码 //如果进位标志位为true,那么要判断当前位置是否还需要进位 //如果取反之后再加上进位的1后为0,那么需要进位 boolean carry_flag = true; if(c_num<0){ for(int i = 0;i<31;i++){ if(carry_flag){ if(bools[i]) carry_flag = false; }else bools[i] = !bools[i]; } bools[31] = true; } //----------------------------- //找到num的二进制数组中的第一个为1的位置 /* int index = -1; for(int i = 31;i>=0;i--){ if(bools[i] == true){ index = i; break; } } */ //循环拼接二进制字符串 for(int i = 31;i>=0;i--){ if(bools[i] == true){ s.append(1); flag++; }else{ s.append(0); } } if(c_num == 0) return c_num + "\t二进制为:" + 0000 + "\t总共 :"+ flag +"\t个1"; else return c_num + "\t二进制为:" + s.toString()+ "\t总共 :"+ flag +"\t个1"; } }运行结果如下:
自己的想法就是这样实现,暂时没有想到更简单的方法,如果有更巧妙简单的方法或者有什么错误的地方,请大家多多指导~~转载请注明出处。~
相关文章推荐
- 面试题-求一个数的二进制数中1的个数(2)
- 【面试题】剑指offer10--求一个数的二进制数中的1的个数
- 【数据结构】(面试题)使用两个栈实现一个队列(详细介绍)
- 面试题1:把一个字符串转换成数字
- 今天看到一个面试题,要求查询一个有姓名,分数,科目的表 变为根据科目分类
- 面试题:求一个字符串中连续出现次数最多的子串
- 面试题:一个方法和类名相同,那这个是构造方法吗?哪些方式可以获取或设置成员变量的值?
- 面试题7:用两个栈实现队列和用两个队列实现一个栈
- 栈和队列的面试题(四)---用两个栈实现一个队列
- 面试题:一个短小强悍的C++面试题---违背Effective C++的条款37:绝不重新定义继承而来的缺省参数值
- 一个C++面试题的数组和STL解法
- 一个SQL存储过程面试题(比较简单)
- 输出一个数的二进制数的奇数序列和偶数序列(三种方法)
- 一个面试题
- 一个面试题。。。
- 关于unsiged char 的一个面试题
- POJ2080--Calendar以及一个类似的编程面试题
- 【那些年遇到过的面试题】考虑如何将一个vector 赋给另一个vector
- 面试题:写一个不能被继承的类,且能正常使用
- 算法面试题——两个有序数组,将一个数组放入另一个空间很大的数组,要求合并之后依然有序,时间复杂度要求最小,不使用额外的数组。