算法中经常有一些很奇妙的技巧,良好的使用可以节省时间和空间复杂度。 接下来我们介绍算法中常用的两个技巧,位运算和奇偶数。

1 位运算

1.1 n & (n-1)

首先,n&(n-1)可以消除n中的最后一个1,这可以用来对n的1 bit位进行计数。
  • n = 4 = 1 0 0;n-1 = 3 = 0 0 1;
  • n&(n-1) = (1 0 0) & (0 1 1 ) = ( 0 0 0 )
1.1.1 位1的个数
编写一个函数,获取一个正整数的二进制形式并返回其二进制表达式中
示例 1:
输入:n = 11
输出:3
解释:输入的二进制串 1011 中,共有 3 个设置位。
class Solution {
public:
    int hammingWeight(int n) {
        int result = 0;
        while(n){
            n = n & (n-1);
            result++;
        }
        return result;
    }
};
1.1.2 判断一个数是不是 2 的指数
给你一个整数 n,请你判断该整数是否是 2 的幂次方。如果是,返回 true ;否则,返回 false 。 如果存在一个整数 x 使得 n == 2x ,则认为 n 是 2 的幂次方。 示例 1:
输入:n = 1
输出:true
解释:20 = 1
class Solution {
public:
    bool isPowerOfTwo(int n) {
        if (n <= 0) return false;
        return (n & (n-1)) == 0? true : false;
    }
};

1.2 n ^ n = 0, n^0 = n

该位运算成对的数进行异或之后为0,故该位运算可以用来寻找一堆重复元素中的单个元素。
1.2.1 查找只出现一次的元素
给你一个 非空 整数数组 nums ,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。 你必须设计并实现线性时间复杂度的算法来解决此问题,且该算法只使用常量额外空间。
示例 1 :
输入:nums = [2,2,1]
输出:1
class Solution {
public:
    int singleNumber(vector& nums) {
        int result = 0;
        for(auto num:nums){
            result ^= num;
        }
        return result;
    }
};
1.2.2 寻找缺失的元素
给定一个包含 [0, n] 中 n 个数的数组 nums ,找出 [0, n] 这个范围内没有出现在数组中的那个数。 示例 1:
输入:nums = [3,0,1]
输出:2
解释:n = 3,因为有 3 个数字,所以所有的数字都在范围 [0,3] 内。2 是丢失的数字,因为它没有出现在 nums 中。
class Solution {
public:
    int missingNumber(vector<int>& nums) {
        int result = 0;
        result ^= nums.size();
        for(int i=0; i<nums.size(); i++){
            result ^=  i ^ nums[i];
        }
        return result;
    }
};

2 奇偶数