面试题 05.01. 插入
思路:
例 N = 1011 1111, M = 101, i = 2, j = 4
(1<<(j-i+1))-1)<<i = 0001 1100
取反后
1110 0011
对N于运算
1011 1111 & 1110 0011 = 1010 0011
M左移动i位
101 << i = 0001 0100
最后M|N
0001 0100 | 1010 0011 = 1011 0111 = 183
class Solution {
public:
int insertBits(int N, int M, int i, int j) {
int mask = ( (1<<(j-i+1)) -1 ) << i;
mask = ~mask;
N &= mask;
return N|(M<<i);
}
};
面试题 05.02. 二进制数转字符串
class Solution {
public:
string printBin(double num) {
string res = "0.";
int i = 30;
while(num > 0 && i--){
num *= 2;
if(num >= 1){
res.push_back('1');
--num;
}else{
res += "0";
}
}
return num ? "ERROR" : res;
}
};
面试题 05.03. 翻转数位
class Solution {
public:
int reverseBits(int num) {
int count = 0, precount = 0;
int max = 0;
while(num > 0){
if(1 & num){ // 最右一位是1
++count;
}else{ // 最右一位是0
if(count + precount + 1 > max){
max = count + precount + 1;
}
precount = count;
count = 0;
}
num >>= 1;
}
if(count + precount + 1 > max){
max = count + precount + 1;
}
return max;
}
};
面试题 05.04. 下一个数
非常好理解的一种思路,双100%:
先计算num的1的个数,然后num分别从num-1和num+1开始减小和增加,直到找到第一个1的个数等于num的值就break。
class Solution {
public:
vector<int> findClosedNumbers(int num) {
int bigger = -1, smaller = -1;
int numOne = countOne(num);
// find bigger
for(int i=num+1; i<=2147483647; ++i){ // 初始值是num+1
if(countOne(i) == numOne){
bigger = i;
break;
}
}
// find smaller
for(int i=num-1; i>=0; --i){ // 初始值是num-1
if(countOne(i) == numOne){
smaller = i;
break;
}
}
return {bigger, smaller};
}
int countOne(int num){
int carry = 1;
int numOne = 0;
for(int i=0; i<31; ++i){
if(num & carry){
++numOne;
}
carry <<= 1;
}
return numOne;
}
};
面试题 05.06. 整数转换
整数转换。编写一个函数,确定需要改变几个位才能将整数A转成整数B。
class Solution {
public:
int convertInteger(int A, int B) {
int count = 0;
if(A == B){
return count;
}
uint32_t num, a, b; // 务必转换成无符号数才行
a = A;
b = B;
num = A ^ B; // 异或大法好
// 统计1的个数:在不知道多少位的时候,用这个方法靠谱
while(num){
num &= num-1; // 去掉最右边的一个1
++count;
}
// 统计1的个数:如果没有明确说是32位数字,还是不要默认32位,否则出错
// int carry = 1;
// for(int i=0;i<31;++i){
// if(num & carry){
// ++count;
// }
// carry <<= 1;
// }
return count;
}
};
这个STL的方法也可以:
class Solution {
public:
int convertInteger(int A, int B) {
bitset<32> ret(A ^ B);
return ret.count();
}
};
面试题 05.07. 配对交换
class Solution {
public:
int exchangeBits(int num) {
int odd = num & 0x55555555; // 用于提取奇数位
int even = num & 0xaaaaaaaa; // 用于提取偶数位
odd <<= 1;
even >>= 1;
return odd | even;
}
};
面试题 05.08. 绘制直线
核心思想:定位到开始和结束的int,将这两个和其中间的所有int全部置为-1,然后再把线段两侧位置(两个int)通过位移运算置为0。
由题意得我们需要构建一个vector数组,int数组内形成一长串比特位,既:
/0(共32个0)0/0(共32个0)0/0(共32个0)0/… 共w/32个int,此为一行。
/0(共32个0)0/0(共32个0)0/0(共32个0)0/… 共w/32个int,此为一行。
/0(共32个0)0/0(共32个0)0/0(共32个0)0/… 共w/32个int,此为一行。
/…
/…共 length/每行int数 行。
通过题目后会变成:在某一行上,
/…
/n(n大于等与0)个0, 至少2个1 ,n(n大于等与0)个0/
/…
我们先大体定位到直线两个点所经过的int,将这些int全部置为0xffffffff,即-1;
让后我们通过x1,x2精准定位开始点所在的int左边需要空出几个0,结束点所在的int右边需要空出几个0。
然后通过位移运算把需要的0给空出来。
当x1与x2在同一个int上时,需要在右移时多移动 右边需要空出几个0 的位置,
以便把右边的1挤掉,然后通过左移再移回去,借以实现空出右边0的目的。
位移时应注意对有符号的整数进行右位移时,会以这个整数的符号补最左边的位置,
详情请google逻辑位移与算数位移。
class Solution {
public:
vector<int> drawLine(int length, int w, int x1, int x2, int y) {
vector<int> p(length, 0);
if(length == 0){
return p;
}
int intNum_in_oneRow = w / 32; // 1行有多少个int
int start = intNum_in_oneRow * y + x1 / 32; // 线段头所在的int
int zeroNum_start = x1 % 32; // 线段头所在的int的左边需要空出的0的个数
int end = intNum_in_oneRow * y + x2 / 32; //线段尾所在的int
int end_zeroNum = 31 - x2 % 32; // 线段尾所在的int的右边需要空出的0的个数
for(int i=start; i<=end; ++i){
p[i] = 0xffffffff; // 设置为-1,反映在二进制数上,就是全是1
}
if(start == end){ // 对头尾在一个int中的情况单独处理:当x1与x2在同一个int上时,需要在右移时多移动 右边需要空出几个0 的位置,以便把右边的1挤掉,然后通过左移再移回去,借以实现空出右边0的目的。
p[start] = (unsigned int) p[start] >> zeroNum_start + end_zeroNum;
p[end] = (unsigned int) p[end] << end_zeroNum;
}else{
p[start] = (unsigned int) p[start] >> zeroNum_start;
p[end] = (unsigned int) p[end] << end_zeroNum;
}
return p;
}
};