通过位操作将数字加1
位操作使用按位运算符(如 AND(&)、OR(|)、NOT(~)、XOR(^)、左移(<<) 和右移(>>))对位流进行逻辑运算以获得所需结果。使用按位运算符是有益的,因为我们可以操作单个位,并且它们比其他运算符更快。
问题陈述
给定一个数字。仅使用按位运算符将数字加1。(不要使用像 ‘+’、‘-’、‘*’ 或 ‘/’ 这样的算术运算符)
方法一:使用反码/非运算符
按位补码/反码是使用 NOT(~) 运算符实现的。
对于数字 n,n 的按位补码,即 ~n = -(n+1)。因此,我们可以通过仅取 ~n 的值并忽略符号来得到 n+1。这可以使用 abs() 函数来完成,该函数返回变量的值并忽略符号。
伪代码
procedure inc (num) num = |~n| // | | represents the abs() function end procedure
示例
在下面的程序中,我们通过使用按位补码运算符和绝对值函数来计算数字加1的结果。
#include <bits/stdc++.h> using namespace std; // Function for finding the increment of input by 1 int inc(int num){ // using the abs() function to get the value obtained by applying the ~ operator num = abs(~num); return num; } int main(){ int input = 7; cout << input << " + 1 using bit manipulation = " << inc(input); return 0; }
输出
7 + 1 using bit manipulation = 8
时间复杂度 − O(1),因为没有实现循环。
空间复杂度 − O(1),因为没有使用额外的空间。
方法二
来看一些二进制加法的例子,如下所示:
从上面的例子可以看出,在对1进行二进制加法时,数字右边第一个未设置位的左边二进制位保持不变。但是,右边第一个未设置位被设置,右边所有的位都被反转,即设置位被取消设置,未设置位被设置(0 转换为 1,1 转换为 0)。
因此,我们可以将我们的问题分解成两个基本部分:
设置右边第一个未设置位。
反转右边所有的位。
递归方法伪代码
procedure inc (num) if num & 1 == 0 ans = num | 1 else ans = inc (num >> 1) << 1 end if end procedure
示例
在下面的程序中,我们使用递归根据上面解释的思想将数字加1。
#include <bits/stdc++.h> using namespace std; // Function for finding the increment of input int inc(int num){ // check if the LSB is set or not. If it is unset, it enters if otherwise, it goes to else if ((num & 1) == 0) { // LSB is set return num | 1; } else { // The number is right shifted and the function is called again // After obtaining the return number from called function, the number is left shifted return inc(num >> 1) << 1; } } int main(){ int input = 14; cout << input << " + 1 using bit manipulation = " << inc(input); return 0; }
输出
14 + 1 using bit manipulation = 15
时间复杂度 − O(k),其中 k 是从右边第一个未设置位的位数。
空间复杂度 − O(k),由于递归堆栈空间。
迭代方法伪代码
procedure inc (num) m = 1 while num & m num = num & (~m) m = m << 1 end while num = num | m end procedure
示例
在下面的程序中,我们使用一个掩码数字,该数字在每次迭代中都会更改,直到我们得到右边第一个未设置位。发现它后,将设置未设置位,并将右边所有其他位反转。
#include <bits/stdc++.h> using namespace std; // Function for finding the increment of input int inc(int num){ // declare a mask with value 1 which is used to find the first unset bit from right side int m = 1; // checks if LSB is set. If set enter the loop and repeat till we get to the unset bit while (num & m) { num &= ~m; // mask is changed at every iteration to set the unset bit and toggle bits when the loop ends m <<= 1; } // mask used to set the unset bit and toggle all the bits on the right-hand side num |= m; return num; } int main(){ int input = 14; cout << input << " + 1 using bit manipulation = " << inc(input); return 0; }
输出
14 + 1 using bit manipulation = 15
时间复杂度 − O(k),其中 k 是从右边第一个未设置位的位数。
空间复杂度 − O(1)
方法三
在这种方法中,我们试图通过减少查找第一个未设置位的时间来降低时间复杂度。在这种方法中,我们将首先找到从右边第一个未设置位,设置它并反转右边所有的位。
伪代码
procedure inc (num) pos = log2 (num & ~num) add = 1 << pos num = num | add if pos != 0 toggle = (1 << pos) - 1 num = num ^ toggle end if ans = num end procedure
示例
在下面的程序中,使用 Log2() 操作来查找未设置位,左移和 OR 来设置位,以及左移和 XOR 来反转位。
#include <bits/stdc++.h> using namespace std; // Function for finding the increment of input int inc(int num){ // finds the position of the first unset bit from right int pos = log2(num & ~num); // the following 2 operations combined set the unset bit int add = 1 << pos; num |= add; // Toggling bits only for the numbers where the first unset bit was not the LSB if (pos != 0){ // Using left shift and XOR operations to toggle bits int toggle = (1 << pos) - 1; num ^= toggle; } return num; } int main(){ int input = 14; cout << input << " + 1 using bit manipulation = " << inc(input); return 0; }
输出
14 + 1 using bit manipulation = 15
时间复杂度 − O(1)
空间复杂度 − O(1)
结论
总之,为了将数字加1,我们可以使用反码概念,或者通过分析加1时位的性质,我们可以遵循上述方法来获得所需的结果。