位运算专题

LeetCode 231:给定一个整数,编写一个函数来判断它是否是 2 的幂次方。

示例 1:

输入: 1
输出: true
解释: 20 = 1

示例 2:

输入: 16
输出: true
解释: 24 = 16

示例 3:

输入: 218
输出: false

方法一:

  思路:此题判断该数字二进制中1的个数,如果非负,其中1的位数只有一位就一定是2的幂,否则就不为2的幂,所以可以采取一位一位的判断比较,但是这样就更慢,所以可以采取其他办法,即n&(n-1)一定可以消去右边第一个出现的1;

  大家可以这样理解,不管对于n中最后一个一的位置如何比如为....1000...(N个)假设1的后面有N个0,对于该数字减一而言就变成了....01111....(N个)后面的0全部就因为借位变成了1,此时将两个数字相与就会把最后一个1之后(包括1)全部变成了0.所以我们消去一个1之后如果还有1 那么这个数一定不为0,所以只需要进行与运算之后判断是否为0即可。

1  クラスソリューション 
 2  {
 3  公共4      BOOL isPowerOfTwo(INT N) 
 5      {
 6          場合(N <= 0を返す 7          リターン(N・(N- 1))== 0 8      }
 9 }。

 方法2:

  アイデア:2の最大整数整数累乗2 ^ 30であり、全ての電力モジュロ2の数はゼロであり、それは2非ゼロの電力ではありません。これはトリッキーな方法です。

1  クラスソリューション 
 2  {
 3  公共4      BOOL isPowerOfTwo(INT N) 
 5      {
 6          リターン N> 0 &&(1 << 30)%N == 0 7      }
 8 }。

方法3:

  思考:Xは、コンピュータ記憶された元のコードの整数であり;関係を見つけ、次に、それは、5を実験するために使用することができる-5&5を作り、そして、-X-格納されている(反転元のコードを加えたもの)の補数です。

    わずか1 2のパワーが存在することになるので、そうあり、X&-x == Xので、計算は、x 1に等しいバイナリX&-x内部右端を表していることを結論することができます。

    xは、複数の数字である場合、それはX&-x <Xです。

  コンピュータ表現は、コンピュータは、減算ではなく加算器であろう主な理由は、負の数を補完するので、これは減算関数は、加算器を実現するために使用され表します。

1  クラスソリューション 
 2  {
 3  公共4      ブール isPowerOfTwo(int型N)
 5      {
 6          リターン N> 0 &&(N&-n)== N。
7      }
 8 }。

LeetCode 762:

  整数の素数に設定されたビットの数をカウントする、範囲内の閉じた間隔[L、R&LT]を見つけるために、二つの整数LとR与えられました。

  (例えば、番号1のバイナリ表現を表す計算セットは、21のバイナリ表現が設定されている3 10101計算であることに留意されたい。また、1は素数ではありません。)

例1:

入力:L = 6、R = 10
出力:4の
説明:
6 - >(2素数である設定されている計算2)110
7 - > 111(設定されている計算3が、3は素数である)
9 - > 1001(2組を計算する、素数は2)であり、
> 1010 10-(計算値2が設定され、素数は2です)

例2:

入力:L = 10、R = 15
出力:5
解説:
10 - > 1010(設定されている計算2,2が素数である)
11 - > 1011(設定されている計算3,3が素数である)
12 - > 1100(2 )のセットを計算する、2は素数である
(計算値3は3は素数であり、設定されている)> 1101から1113を
(計算3は3は素数であり、設定されている)> 1110年から1114年
15 - > 1111(計算値4が設定され、 4)が素数ではありません

注意:

Lは、Rは、L <= Rであり、[1、10 ^ 6]の整数です。
R - Lの最大値は10000です。

アイデア:

  しない素数であると判定されているデータ1の数を計算するステップと、10 ^ 6の最大データセクションは、最初の素数は20未満を占め、以下2 ^ 20です。

1  クラスソリューション
 2  {
 3  公共4      INT countPrimeSetBits(int型 L、INT R)
 5      {
 6          unordered_set < INT >素数({ 235711131719 });
7          int型のres;
8          のためには、int型 = L Iと、iが<= R; iは++ 9          {
 10              INTをS = 0 ;
 11              用のint型 K = I; K; = K >> 1)= K S + 1 ;
 12は、             IF(primes.count(S))RES ++ // COUNT()sは素数を見つけるために使用されます。番号。
13は         }
 14の         リターンRES;
 15      }
 16 }。

LeetCode 136:デジタル私は一度だけ表示されます

  整数の非空の配列を考えると、要素に加えて、一度だけ表示され、各要素の残りの部分は二回表示されます。要素のみに一度表示されていることを知るために。説明:あなたのアルゴリズムは線形時間複雑性を持っている必要があります。あなたはそれを達成するために余分なスペースを使用することはできませんか?

例1:

入力:[2,2,1] 
出力:1

例2:

入力:[4,1,2,1,2] 
出力:4

  思路:a ^ a = 0;

     0 ^ A = A。

 1 class Solution 
 2 {
 3 public:
 4     int singleNumber(vector<int>& nums)
 5     {
 6         int res = 0;
 7         for(auto x : nums) res ^= x;
 8         return res;
 9     }
10 };

LeetCode 476:

  给定一个正整数,输出它的补数。补数是对该数的二进制表示取反。

注意:

  给定的整数保证在32位带符号整数的范围内。你可以假定二进制数不包含前导零位。

示例 1:

输入: 5
输出: 2
解释: 5的二进制表示为101(没有前导零位),其补数为010。所以你需要输出2。

示例 2:

输入: 1
输出: 0
解释: 1的二进制表示为1(没有前导零位),其补数为0。所以你需要输出0。

方法一: 

  ^ B = 0001 1010は否定され、Bは、1111 1111 1110 0101であると仮定する。すなわち、ビットとNUMの同じ数、及び1つのTMPとNUMの整数であるか、または所望されます。

1  クラスソリューション 
 2  {
 3  公共4      INT findComplement(INT NUM) 
 5      {
 6          int型 TMP = 1 7          一方(TMP < NUM)
 8          {
 9              TMP << = 1 10              TMP + = 1 ;
11          }
 12          リターン(TMP ^ NUM)。
13      }
 14 }。

方法2:

  順次否定シフト。

1  クラスソリューション 
 2  {
 3。 公共4      INT findComplement(INT NUM) 
 5      {
 6          INT RES = 0、T = 0 ;
 7          一方(NUM)
 8          {
 9              (!&NUM RES + = 1)<< T; //これを採取し、服用しないでなければなりません! -から!動作は、それが1が0であり、0であり; -演算、全てのビットが反転されます。
10              NUM = >> 1。;
 11              T ++ ;
 12である         }
 13は、         リターンRES。
14     }
15 };

LeetCode 137: 只出现一次的数字II

  给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次。找出那个只出现了一次的元素。

说明:你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?

示例 1:

输入: [2,2,3,2]
输出: 3

示例 2:

输入: [0,1,0,1,0,1,99]
输出: 99

方法一:(最优做法)

思路:(状态机的思路)(没太明白)

  二进制下不考虑进位的加法:136 题中我们用到了异或运算。实际上,异或运算的含义是二进制下不考虑进位的加法,

    即:0 ^ 0 = 0 + 0 = 0   0 ^ 0 = 0 + 0 = 0,

      0 ^ 1 = 0 + 1 = 1  0 ^ 1 = 0 + 1 = 1,

      1 ^ 0 = 1 + 0 = 1 1 ^ 0 = 1 + 0 = 1,

      1 ^ 1 = 1 + 1 = 0  1 ^ 1 = 1 + 1 = 0(不进位)。

  三进制下不考虑进位的加法:通过定义某种运算 #,使得 0 # 1 = 1,1 # 1 = 2,2 # 1 = 0。在此运算规则下,出现了 3 次的数字的二进制所有位全部抵消为 0,而留下只出现 1 次的数字二进制对应位为 1。

    因此,在此运算规则下将整个 arr 中数字遍历加和,留下来的结果则为只出现 1 次的数字。

 

 1 class Solution 
 2 {
 3 public:
 4     int singleNumber(vector<int>& nums)
 5     {
 6         int ones = 0, twos = 0;
 7         for(auto x : nums)
 8         {
 9             ones = (ones ^ x) & ~twos;
10             twos = (twos ^ x) & ~ones;
11         }
12         return ones;
13     }
14 };

 方法二:(一般做法)

   分别对所有数据的每一位进行对3取余操作。

 1 class Solution 
 2 {
 3 public:
 4     int singleNumber(vector<int>& nums)
 5     {
 6         int ans = 0;
 7         for(int bit = 0; bit < 32; bit++)
 8         {
 9             int counter = 0;
10             for(int i = 0; i < nums.size(); i++)
11             {
12                 counter += (nums[i] >> bit) & 1;
13             }
14             
15             ans += (counter % 3) << bit;
16         }
17         return ans;
18     }
19 };

 LeetCode 260:只出现一次的数据III

  给定一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。

示例 :

输入: [1,2,1,3,2,5]
输出: [3,5]
注意:结果输出的顺序并不重要,对于上面的例子, [5, 3] 也是正确答案。你的算法应该具有线性时间复杂度。你能否仅使用常数空间复杂度来实现?

   思路:

    第一步:
      把所有的元素进行异或操作,最终得到一个异或值。因为是不同的两个数字,所以这个值必定不为0;

    第二步:
      取异或值最后一个二进制位为1的数字作为mask,如果是1则表示两个数字在这一位上不同。

    第三步:
      分组,就可以找出两个数。

 1 class Solution 
 2 {
 3 public:
 4     vector<int> singleNumber(vector<int>& nums) 
 5     {
 6         int s = 0;
 7         for(auto x : nums) s ^= x;
 8         
 9         int k = 0;
10         while(!(s >> k & 1)) k++;
11         
12         int s2 = 0;
13         for(int x : nums)
14         {
15             if(x >> k & 1)
16                 s2 ^= x;
17         }
18         /*
19             s = a ^ b;
20             s2 = a;
21             s ^ s2 = a ^ b ^ a = b;
22         */
23         
24         return {s2, s2 ^ s}; // vector<int> ({s2, s2 ^ s});
25     }
26 };

 LeetCode 371:不使用运算符 + 和 - ​​​​​​​,计算两整数 ​​​​​​​a 、b ​​​​​​​之和。

示例 1:

输入: a = 1, b = 2
输出: 3

示例 2:

输入: a = -2, b = 3
输出: 1

 思路:a ^ b 是不进位加法;a & b 只有在相对应的位都为1的时候才为1,所以a & b 表示有进位的位;所以,结果等于 a ^ b + (a & b) << 1;

 1 class Solution
 2 {
 3 public:
 4     int getSum(int a, int b)
 5     {
 6         if(!b) return a;
 7         int sum = a ^ b, carry = (unsigned int)(a & b) << 1; // 这里将a&b强转unsigned int,是因为有的不支持负数左移
 8         return getSum(sum, carry);
 9     }
10 };

 LeetCode 201:给定范围 [m, n],其中 0 <= m <= n <= 2147483647,返回此范围内所有数字的按位与(包含 m, n 两端点)。2147483647 这个数是int的最大值。

示例 1: 

输入: [5,7]
输出: 4

示例 2:

输入: [0,1]
输出: 0

思路:

  判断m、n是否相等,如果不相等,m+1会使m的二进制数末位进位,有进位说明m的末位肯定有0的情况,0与任何数相与皆得0,所以结果的末位肯定是0。

  同理,不断右移1位进行比较,直到最终 m=n 时,说明找到了[m,n]这个范围内高位没有变化的数,左移相同位数得到的结果就是所求的值。

法一:

class Solution
{
public:
    int rangeBitwiseAnd(int m, int n)
    {
        int count = 0; // 统计移位次数
        while (m != n)
        {
            m >>= 1;
            n >>= 1;
            count++;
        }
        n <<= count;
        return n;
    }
};

法二:

 1 class Solution 
 2 {
 3 public:
 4     int rangeBitwiseAnd(int m, int n) 
 5     {
 6         while(n>m) 
 7             n = n&(n-1);
 8         return n;
 9     }
10 };

 

おすすめ

転載: www.cnblogs.com/WPF-342201/p/11426944.html