Q5.1 插入
插入。给定两个32位的整数N与M,以及表示比特位置的i与j。编写一种方法,将M插入N,使得M从N的第j位开始,到第i位结束。假定从j位到i位足以容纳M,也即若M = 10 011,那么j和i之间至少可容纳5个位。例如,不可能出现j = 3和i = 2的情况,因为第3位和第2位之间放不下M。
示例1:
输入:N = 1024(10000000000), M = 19(10011), i = 2, j = 6
输出:N = 1100(10001001100)
示例2:输入: N = 0, M = 31(11111), i = 0, j = 4
输出:N = 31(11111)
/*
* 给定两个32位的整数N和M,以及表示比特位置的i和j。编写一个方法,将M插入到N中,
* 使得M从N的第j位开始,到第i位结束,假定从j位到i位足以容纳M,也即是M=10011
* 那么j和i之间至少可以容纳5个数,假如,不可能出现j=3,i=2的情况,因为第三位和第二位之间放不下M
* 例如
* N=1000000000(1024)
* M=10011(19)
* i=2,j=6,输出10001001100
* 思路如下:
* 1.将N中的从j到i之间清零
* 2.对M执行移位操作与j和i之间的位对其
* 3.合并M和N
* */
public int updateBits(int n,int m,int i,int j)
{
int allOnes=~0;//创建一连串1 假如为11111111
int left=allOnes<<(j+1); //在位置j之前的位均值为1,其余为0,此刻为11100000
int right=((allOnes<<i)-1);//在位置i之后的位均置位1,此刻为00000011
int mask=left | right; //进行位或运算之后得到 11100011
int n_cleared=n& mask; //清除位置j到i的位,然后将M放进去
int m_shifted=m<<i; //将M移动到相应的位置
return n_cleared | m_shifted; //对两者进行位或操作
}
public static void main(String[] args) {
// TODO Auto-generated method stub
BitGet bg = new BitGet();
int num=bg.updateBits(1024, 19, 2, 6);
System.out.println(Integer.toBinaryString(num));
}
Q5.2 二进制数转字符串
二进制数转字符串。给定一个介于0和1之间的实数(如0.72),类型为double,打印它的二进制表达式。如果该数字无法精确地用32位以内的二进制表示,则打印“ERROR”。
示例1:
输入:0.625
输出:"0.101"
示例2:输入:0.1
输出:"ERROR"
提示:0.1无法被二进制准确表示
提示:32位包括输出中的"0."这两位。
- 浮点数乘以进制2,取整数位添加到 二进制小数位后面
- 浮点数舍去整数位,重复以上过程直至浮点数变成 0,若无法等于 0,则无法精确表示
另参考:二进制小数 转 10进制
举例:将 二进制0.1111转换成 十进制数
二进制 0 . 1 1 1 1
----------------------------------------------------------------------------------------------------------------
换算次方 2^(0) 2^(-1) 2^(-2) 2^(-3) 2^(-4)
----------------------------------------------------------------------------------------------------------------
换成分数 0/2 1/2 1/4 1/8 1/16
----------------------------------------------------------------------------------------------------------------
换成十进制 0 + 0.5 + 0.25 + 0.125 + 0.0625 = 0.9375
class Solution {
public String printBin(double num) {
StringBuilder builder = new StringBuilder("0.");
for (int i = 0; i < 33; i++) {
num *= 2;
if (num >= 1) {
// 取整数部分
builder.append("1");
// 取完后减1取小数部分
num -= 1;
} else {
builder.append(0);
}
if (num == 0) {
return builder.toString();
}
}
return "ERROR";
}
}
Q5.3 翻转数位
示例 1:
输入: num = 1775(11011101111)
输出: 8示例 2:
输入: num = 7(0111)
输出: 4
Java简单迭代:
curLen记录当前长度,
preLen记录上次翻转后的长度,
因为只能翻转1次,所以需要再次翻转时curLen要减去preLen。
class Solution {
public int reverseBits(int num) {
int maxLen = 0, preLen = 0, curLen = 0, bits = 32;
while (bits-- > 0) {
if ((num & 1) == 0) {
curLen -= preLen;
preLen = curLen + 1;
}
curLen++;
maxLen = Math.max(maxLen, curLen);
num >>= 1;
}
return maxLen;
}
}
Q5.4 下一个数
下一个数。给定一个正整数,找出与其二进制表达式中1的个数相同且大小最接近的那两个数(一个略大,一个略小)。
示例1:
输入:num = 2(或者0b10)
输出:[4, 1] 或者([0b100, 0b1])
示例2:输入:num = 1
输出:[2, -1]
提示:num的范围在[1, 2147483647]之间;
如果找不到前一个或者后一个满足条件的正数,那么输出 -1。
手写下一个排列、前一个排列
public int[] findClosedNumbers(int num) {
int up = num + 1;//向上枚举
int down = num - 1;//向下枚举
int count = findOneCount(num);//num的1的个数
while (findOneCount(up) != count) {
up++;
if (up < 0) {//越界了那就是找不到,设置为-1
up = -1;
break;
}
}
while (findOneCount(down) != count) {
down--;
if (down < 0) {//变为负数了那就是找不到了,设置为-1
down = -1;
break;
}
}
return new int[]{up, down};
}
//求数的二进制1的个数
private static int findOneCount(int num) {
int count = 0;
while (num != 0) {
num &= num - 1;
count++;
}
return count;
}
Q5.6 整数转换
整数转换。编写一个函数,确定需要改变几个位才能将整数A转成整数B。
示例1:
输入:A = 29 (或者0b11101), B = 15(或者0b01111)
输出:2
示例2:输入:A = 1,B = 2
输出:2
提示:A,B范围在[-2147483648, 2147483647]之间
class Solution {
public int convertInteger(int A, int B) {
int x = A ^ B, cnt = 0;
while (x != 0) {
x &= (x - 1);
cnt++;
}
return cnt;
}
}
Q5.7 配对交换
配对交换。编写程序,交换某个整数的奇数位和偶数位,尽量使用较少的指令(也就是说,位0与位1交换,位2与位3交换,以此类推)。
示例1:
输入:num = 2(或者0b10)
输出 1 (或者 0b01)
示例2:输入:num = 3
输出:3
分别用01交替的二进制数,将原数字的奇偶位取出
偶数位右移1位,奇数位左移1位
再 |
合并
class Solution {
public:
int exchangeBits(int num) {
int even = 0b10101010101010101010101010101010;//0xAAAAAAAA
int odd = 0b1010101010101010101010101010101;//0x55555555
return ((num&even)>>1) | ((num&odd)<<1);
}
};
Q5.8 绘制直线
绘制直线。有个单色屏幕存储在一个一维数组中,使得32个连续像素可以存放在一个 int 里。屏幕宽度为w,且w可被32整除(即一个 int 不会分布在两行上),屏幕高度可由数组长度及屏幕宽度推算得出。请实现一个函数,绘制从点(x1, y)到点(x2, y)的水平线。
给出数组的长度 length,宽度 w(以比特为单位)、直线开始位置 x1(比特为单位)、直线结束位置 x2(比特为单位)、直线所在行数 y。返回绘制过后的数组。
示例1:
输入:length = 1, w = 32, x1 = 30, x2 = 31, y = 0
输出:[3]
说明:在第0行的第30位到第31为画一条直线,屏幕表示为[0b000000000000000000000000000000011]
示例2:输入:length = 3, w = 96, x1 = 0, x2 = 95, y = 0
输出:[-1, -1, -1]
一行有几个int,n = w/32 个
从 y 行开始,那么起始下标 idx = y * n
class Solution {
public int[] drawLine(int length, int w, int x1, int x2, int y) {
int[] arr = new int[length];
for (int i = x1; i <= x2; ++i) {
// find the correct arr element
int index = (w / 32) * y + (i / 32);
// find the correct bit to set, note from MSB to LSB
arr[index] |= (1 << (31 - i % 32));
}
return arr;
}
}