刷题计划——位算法(一)

461. 汉明距离(简单)

题目:

两个整数之间的汉明距离指的是这两个数字对应二进制位不同的位置的数目。

给出两个整数 x 和 y,计算它们之间的汉明距离。

注意:
0 ≤ x, y < 231

示例:

输入: x = 1, y = 4

输出: 2

解释:
1   (0 0 0 1)
4   (0 1 0 0)
       ↑   ↑

上面的箭头指出了对应二进制位不同的位置。

有多种思路可以解决这一问题,但最基础的海试通过与或运算之后的移位运算来解决。

  1. 通过将x,y进行与或运算之后,所得结果数的每个数为1的位,必定是x与y不同的位。
  2. 此时进行移位操作,判断为1位数的个数
class Solution {
public:
    int hammingDistance(int x, int y) {
        y = x^y;
        int res = 0;
        while(y != 0){
            res += y&1 ? 1: 0;
            y >>= 1;
        }

        return res;
    }
};

191. 位1的个数(简单)

题目:

编写一个函数,输入是一个无符号整数,返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为汉明重量)。

示例 1:

输入:00000000000000000000000000001011
输出:3
解释:输入的二进制串 00000000000000000000000000001011 中,共有三位为 '1'。
示例 2:

输入:00000000000000000000000010000000
输出:1
解释:输入的二进制串 00000000000000000000000010000000 中,共有一位为 '1'。
示例 3:

输入:11111111111111111111111111111101
输出:31
解释:输入的二进制串 11111111111111111111111111111101 中,共有 31 位为 '1'。
 

提示:

请注意,在某些语言(如 Java)中,没有无符号整数类型。在这种情况下,输入和输出都将被指定为有符号整数类型,并且不应影响您的实现,因为无论整数是有符号的还是无符号的,其内部的二进制表示形式都是相同的。
在 Java 中,编译器使用二进制补码记法来表示有符号整数。因此,在上面的 示例 3 中,输入表示有符号整数 -3

无须赘述,直接将每个位移位并与1进行按位与运算,统计结果并返回。

class Solution {
public:
    int hammingWeight(uint32_t n) {
        int res = 0;
        while(n != 0){
            res += n&1? 1: 0;
            n >>= 1;
        }

        return res;
    }
};

136. 只出现一次的数字(简单)

题目:

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

说明:

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

示例 1:

输入: [2,2,1]
输出: 1
示例 2:

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

很明显,这是一道可以用map解决的问题;通过一个为nums[i] 的索引,储存num[i]出现的次数count,遍历map得到为1的那个num[i]。

不过由于题目要求时间复杂度为O(N),空间复杂度在O(1),因此必须在一次遍历下借助常数空间完成。

在上面两道例题中,与或运算占据着重要的地位,因此应考虑可以通过位运算来解决此问题。 与或运算的重要性质是:当一个数在与自身进行与或运算之后,其结果必定等于0。借助这一重要性质,通过题意“除了某个元素只出现一次以外,其余每个元素均出现两次”,即证明,可以通过与或运算来解决这一问题。

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int sign = 0;
        for(auto num: nums){
            sign = sign ^ num;
        }

        return sign;
    }
};

137. 只出现一次的数字 II(中等)

题目:

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

示例 1:

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

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

同上题一样,要求的时间复杂度为O(N),空间复杂度在O(1);不同的是,此时只有一个数字出现1次,其余数字全部出现3次。

上题分析出,在出现两次相同数之后,这一性质可以拓展为一个数出现偶数次之后,其与或运算的结果为0。如此看来,似乎与或运算在此题失去了作用。

在136题中,若给定数组为**[2, 2, 1, 3, 3]**,其结果为:

res num
10 10
00 10
01 01
10 11
01 11

返回最终的res 01, 就是只出现一次的数字。

而137题中,使用类似的办法:
示例1 [2, 2, 3, 2]

once twice num
10 00 10
00 10 10
11 10 11
11 00 10

通俗来说,就是出现一次的数在once中存储,出现两次的数在twice中存储,但需要注意的是,在136例中res的状态由res和num决定,而在137题中:once的状态由num,once与twice共同决定,而twice的状态同样由num,twice与once共同决定

  1. 当一个数字出现时,判断其在once中是否出现,若在once中出现,将其与twice进行与或运算,同时在once中将该数字“清除”; 若在twice中出现,将其与once进行与或运算,同时在once,twice中将该数字“清除”。(注意,在一个第一次出现时,此时该数既不在once中出现,也不在twice中出现)
  2. 最终只有一个数只在once中出现一次,once就是该数
class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int once = 0, twice = 0;
        for(int num: nums){
            once = ~twice & (once^num);
            twice = ~once & (twice^num);
        }

        return once;
    }
};

你可能感兴趣的:(C++,算法)