lintcode题目链接:更新二进制位
描述
给出两个32位的整数\( N \)和\( M \),以及两个二进制位的位置\( i \)和\( j \)。写一个方法来使得N中的第\( i \)到\( j \)位等于\( M \)(\( M \)会是\( N \)中从第\( i \)位开始到第\( j \)位的子串)
在该函数里给出的\( N \)和\( M \)都是十进制位,同时返回的答案也应该是十进制位
保证从\( i \)到\( j \)的二进制可以完全覆盖\( M \)。例如,如果\( M=10011 \),那么可以保证在\( i \)和\( j \)之间至少有5位。 类似\( j=3 \)和\( i=2 \)不会出现,因为\( M \)无法完全覆盖二进制的第二位和第三位。
样例
样例1:
输入: N=(10000000000)2 M=(10101)2 i=2 j=6
输出: N=(10001010100)2
样例 2:
输入: N=(10000000000)2 M=(11111)2 i=2 j=6
输出: N=(10001111100)2
解答:
吐槽:纯二进制位运算。
整体思路:
假设\( n = 1110101110, m = 10101, i = 2, j = 6 \),
我们通过将\( n \)和\( m \)的二进制转换成以下形式,来实现目标,为了方便理解,我用_分隔。
n1 = 111_00000_10
m1 = 000_10101_00
简单来说,就是把中间的\( j-i+1 \)位变成\( 0 \),然后和 (左移\( i \)位的\( m \)) 做"或"运算。
详细步骤:
- 将\( -1 \) 左动 \( j+1 \)位 , \(-1 = 1111...111 \), 一共32个1, 左移后会在右侧多出\( j+1 \)个\( 0 \)
- 然后将上面的数字, 再 加上 \( 2^i - 1 \), 就是变成111..1110000011, 相当于再最右侧的\( i \)位上全变成\( 1 \)
- 把上面的数 和 \( n \) 做“与”运算,可以将 \( n \)中间的 \( j - i + 1 \)位都变成 0 , 就实现了上面代码框中的\( n1\) , 原理: 任何二进制数与1做“与“运算都等于其自身, 任何二进制数与0做“与“运算都等于0。
- 将\( m \)左移动\( i \)位,变成上面代码框中的\( m1 \)
- 将\( n1 \)和\( m1 \)二者做“或运算”, 原理: 任何二进制数与0做“或”运算都等于其自身。
- 注意坑:如果 \( j=31 \), 第1步中会导致错乱,因为一个数字左移\( 32 \)位不变,所以需要单独判断一下。
public class Solution {
public int updateBits(int n, int m, int i, int j) {
int x = j<31? ((-1) << (j+1)) : 0; // 排坑的代码
return (n & ( x + ((1<<i) - 1))) | (m << i);
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。