JZ11 二进制中1的个数
(中等)
题目
描述
输入一个整数,输出该数32位二进制表示中1的个数。其中负数用补码表示。
示例
输入:
10
返回值:
2
解题过程
1 纯正数版——方法一
此题我首先是按纯正数的情况来进行考虑的,但因为负数涉及补码,因此这种方式很对负数是行不通的,但是我还是想练习一下针对这种问题的编码能力,而且如果只是纯正数(当然,0 的情况特殊可考虑,可不考虑),也可以单独出一道题,也有研究的价值。
对一个十进制正数转为二进制的形式,简单来说就是拆成了多个 2^n 的组合(当然,需是先从高位算起的个数最少的组合),在二进制的相应位中用 有效数1 进行标记,因此,我们可以找出数 n 的最高二进制,同时让 n 减去这个数得到新的 n,再对新的 n 进行重复操作,过程中记录下 n 执行减法的次数即可得知 n 的二进制形式中有几个 1 。
【实现】
public class Solution {
public int NumberOf1(int n) {
int num = 0; // 二进制形式中 1 的个数
while (n > 0) {
int i = 2;
while (i < n) {
i *= 2;
}
if (i != 2 && i != n) { //当 while 循环体执行过,并且 i 多乘了一次 2 时
i /= 2;
}
n -= i;
num++;
}
return num;
}
public static void main(String[] args) {
Solution solution = new Solution();
System.out.println("solution.NumberOf1(10) = " + solution.NumberOf1(10));
System.out.println("solution.NumberOf1(8) = " + solution.NumberOf1(8));
System.out.println("solution.NumberOf1(3) = " + solution.NumberOf1(3));
System.out.println("solution.NumberOf1(1) = " + solution.NumberOf1(1));
}
}
2 纯正数版——方法二
或是用另一种更简洁的方式实现,参数 n 对 2 求余,如果没有余数,则可能是正好的 2 的整数倍,也可能不是,因此只要 n 不等于 0 ,再对 n 进行除以 2 ,在这其中如果出现余数为 1 的情况,则说明当前的 n 值,用 2 进制表示时,1 的数量肯定是多余 1 个的,因此要计数上这种情况的一次,并且,之后 n 因为是整除 2 ,因此最后肯定还会出现 1 次求余为 1 的情况,这也正是当前 n 值(当然不是说初始的 n 值)对应的当前最高位的二进制 1,因此,用这种方法,最终也可以求出正数情况时的二进制中 1 的个数。
【实现】
public class Solution {
public int NumberOf1(int n) {
int ans = 0;
while (n != 0) {
int tmp = n % 2;
if (tmp == 1) ++ans;
n /= 2;
}
return ans;
}
}
3 此题正式版——二进制位移法
直接将整数看成二进制,然后采用与运算、移位的方法。
首先,我们先写一下正数情况下的实现:
【实现】
public class Solution {
public int NumberOf1(int n) {
int ans = 0;
while (n != 0) {
if ((n & 1) == 1) ans++;
n >>= 1;
}
return ans;
}
}
由于正数情况,右移 1 位,左位是补 0 ,因此,可以终止 while 循环,最终得出正确答案,但是如果我们想处理负数的情况,即左位补 1 ,在上述代码中就解决不了,而且 while 就终止不了,况且一直左移只会让 1 的个数越来越多。
因此,我们换种方式,我们用 1 作为移动方,使用初始的 1 不断左移,并与参数 n 进行与运算,这样最终这个初始的 1 是可以将 1 移出左边界,从而变成 0 ,并且也已经将参数 n 中的 1 全部找出,因此,是可行的,代码如下:
【实现】
public class Solution {
public int NumberOf1(int n) {
int ans = 0;
int flag = 1;
while (flag != 0) {
if ((n & flag) != 0) ans++;
flag <<= 1;
}
return ans;
}
}
这个算法可以解决此题,但是需要运行32次(int 型为 4 个字节,因此在内存中占 32 位,并且是将 1 从右向左移出)。
4 此题正式版——技巧法
十分巧妙且高效,题解也解释的十分清楚,我不再过多赘述,只能说发明这个算法的人太强了,并且是十分善于观察和探索的人才能想出来,当然我们也不能排除是偶然的灵感迸发。
JZ12 数值的整数次方
(中等)
题目
描述
给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。
保证base和exponent不同时为0。不得使用库函数,同时不需要考虑大数问题,也不用考虑小数点后面0的位数。
示例1
输入:
2.00000,3
返回值:
8.00000
示例2
输入:
2.10000,3
返回值:
9.26100
示例3
输入:
2.00000,-2
返回值:
0.25000
说明:
2的-2次方等于1/4=0.25
思路
此题根据指数的情况来划分即可。
当指数为 0 时,若底数也为 0 ,因 0 的 0 次幂无意义,因此此情况应提示错误信息;
当指数为正数时,将指数个底数相乘返回结果即可;
当指数为负数时,应将底数转为其倒数,并且,若在循环计算时借助指数与当前下标大小作为循环终止条件,则指数应提前转为其相反数。
实现
public class JZ12数值的整数次方 {
public double Power(double base, int exponent) {
if (exponent == 0) {
if (base == 0) {
try {
throw new Exception("0的0次方无意义");
} catch (Exception e) {
e.printStackTrace();
}
}
return 1;
} else if (exponent > 0) { //指数为正数
if (base == 0) {
return 0;
}
} else if (exponent < 0) { //指数为负数
if (base == 0) {
try {
throw new Exception("当指数为负数,底数为0时无意义");
} catch (Exception e) {
e.printStackTrace();
}
}
base = 1 / base;
exponent = -exponent;
}
double result = 1;
for (int i = 0; i < exponent; i++) {
result *= base;
}
return result;
}
public static void main(String[] args) {
JZ12数值的整数次方 s = new JZ12数值的整数次方();
System.out.println("s.Power(2.00000, 3) = " + s.Power(2.00000, 3));
System.out.println("s.Power(2.00000, 0) = " + s.Power(2.00000, 0));
System.out.println("s.Power(2.00000, -2) = " + s.Power(2.00000, -2));
}
}