前言
如果想进大厂工作,除了基础知识,项目经验,还有一些非常重要的能力需要锻炼,其中最重要的就是算法。算法题千千万,但是类型也就几十种,接下来的系列文章,我将面试中常见的算法题类型进行归纳。
二进制面试题01. 插入
给定两个整型数字 N 与 M,以及表示比特位置的 i 与 j(i <= j,且从 0 位开始计算)。
编写一种方法,使 M 对应的二进制数字插入 N 对应的二进制数字的第 i ~ j 位区域,不足之处用 0 补齐。具体插入过程如图所示。
代码:
public int insertBits(int N, int M, int i, int j) {
// left in (,j+1];middle in [i,j];right in [i-1,0];
int left = N>>j>>1; // 把最左边的部分调整好了,即抛弃了替换部分和低位部分
left = left<<j<<1; // 因此最后要进行或运算,所以把他再移到原来的高位上。
int middle = M<<i; // 替换N的j<-----i位,那么只需要将M左移i位即可
int right = N&((1<<i)-1);// 只需要N的低位,将高位置零,(1<<2)-1 = (11)2
return left | middle | right;
}
二进制面试题02. 二进制数转字符串
二进制数转字符串。给定一个介于0和1之间的实数(如0.72),类型为double,打印它的二进制表达式。如果该数字无法精确地用32位以内的二进制表示,则打印“ERROR”。
进制表示,则打印“ERROR”。
代码:
class Solution {
public String printBin(double num) {
StringBuilder ans = new StringBuilder("0.");
while (num != 0D && ans.length() <= 32) {
num *= 2;
if (num >= 1) {
ans.append(1);
num -= 1;
} else ans.append(0);
}
return ans.length() > 32 ? "ERROR" : ans.toString();
}
}
二进制面试题03. 翻转数位
给定一个32位整数 num,你可以将一个数位从0变为1。请编写一个程序,找出你能够获得的最长的一串1的长度。
示例 1:
输入: num = 1775(110111011112)
输出: 8
示例 2:
输入: num = 7(01112)
输出: 4
思路:
本题使用动态规划解决比较容易:使用用两个动态规划数组 current[] reverse[]
current[i] 表示包含第i位的从num二进制低位至第i位连续1的最长长度
reverse[i] 表示包含第i位的从低位到第i位最多翻转1个0->1 的连续1的最长长度
用num[i]表示整数num第i位的值
当num[i]=1时,current[i] = current[i-1]+1,因为current[i-1]一定包含i-1位,也就是和第i位连续,所以前i-1的最大长度连上第i位的长度就等于current[i],同理reverse[i] = reverse[i-1]+1;
num[i]=0时,连续中断,current[i]=0,而reverse[i]允许翻转1次,但是reverse[i]又必须包含第i位,也就是说只能翻转第i位,所以前面不能出现翻转,必须全是1,这个长度恰好就是current[i-1],所以reverse[i] = current[i-1]+1
遍历num所有位数,也就是32位后,reverse数组中的最大值就是答案。
状态方程:
current[i] = num[i]==1?current[i-1]+1:0
reverse[i] = num[i]==1?reverse[i-1]+1:current[i-1]+1
观察状态方程,我们发现current和reverse第i位只和第i-1位有关,所以可以把动态数组优化成两个变量current和reverse,同时更新最大值max并作为结果返回。
解法只说明答案是取reverse最大值而不涉及current最大值,可以思考一下为什么。 该解法针对任何值均需要循环32次,然而一些比较小的数显然不用遍历32次(比如1)。想一想可不可以优化代码用以对某些小值减少循环次数。
代码:
class Solution {
public int reverseBits(int num) {
int max = 0;
int reverse = 0;
int current = 0;
for(int i=0;i<32;i++){
if((num&1)==1){
current++;
reverse++;
}else{
reverse = current+1;
current = 0;
}
if(reverse>max) max = reverse;// 每遍历1位,reverse就会更新一下
num >>= 1;
}
return max;
}
}
二进制面试题04. 整数转换
整数转换。编写一个函数,确定需要改变几个位才能将整数A转成整数B。
示例1:
输入:A = 29 (或者0b11101), B = 15(或者0b01111)
输出:2
示例2:
输入:A = 1,B = 2
输出:2
提示:
A,B范围在[-2147483648, 2147483647]之间
解题思路:
(1)A与B异或,得到的结果可以用来判断不同的位数,例如:A = 1100, B = 0011, A^B = 1111 我们知道A^B中有几个1,那么就可以判断出有几个位需要转换了
(2)emp&(temp-1) 可以把temp最右边的1置为0,而其他位保持不变,例如:
temp=1100,
temp-1=1100-1=1011
temp&(temp-1) = 1100 & 1011 = 1000
代码:
public int convertInteger(int A, int B) {
// A与B进行异或运算,得到的结果1就是不相同,0就是相同
// 0011^1100 = 1111 结果有几个1,那么就有几个位要改变
int temp = A^B,count = 0;
while (temp != 0){
// temp&(temp -1) 可以把temp最右边的1置0,其余位不变
// 这样,这个操作执行了几次,就是有几位要转换
temp = temp&(temp - 1);
count++;
}
return count;
}
二进制面试题05. 配对交换
配对交换。编写程序,交换某个整数的奇数位和偶数位,尽量使用较少的指令(也就是说,位0与位1交换,位2与位3交换,以此类推)。
示例1:
输入:num = 2(或者0b10)
输出 1 (或者 0b01)
示例2:
输入:num = 3
输出:3
提示:
num的范围在[0, 2^30 - 1]之间,不会发生整数溢出。
class Solution {
public int exchangeBits(int num) {
int[] arr = new int[31];
int bit = 0;
while (num != 0) {
arr[bit++] = num % 2;
num = num / 2;
}
for (int i = 1; i <= bit; i = i + 2) {
int temp = arr[i];
arr[i] = arr[i - 1];
arr[i - 1] = temp;
}
int result = 0;
for(int i = bit; i >= 0;i--){
result = result * 2 + arr[i];
}
return result;
}
}