剑指 Offer 15. 二进制中1的个数(简单)

通过移位运算符以及不停的&1来判断单位数是否为1

  1. class Solution {
  2. public:
  3. int hammingWeight(uint32_t n) {
  4. int ans = 0;
  5. for(int i = 0; i <= 31; i++){
  6. if((n>>i)&1==1){
  7. ans++;
  8. }
  9. }
  10. return ans;
  11. }
  12. };

剑指 Offer 65. 不用加减乘除做加法(重点)

  • 一个关键是递推思想
  • 异或运算^结果相当于二进制相加不进位,&运算结果左移相当于进位结果。每次都将进位结果和非进位结果重新相加,当进位结果位零时输出。
  • c++不支持负数左移,因此转换为无符号数再次左移 ```cpp class Solution { public: int add(int a, int b) {
      while(b!=0){
          int c = (unsigned int)(a&b)<<1;
          a = a ^ b;
          b = c;
      }
      return a ;
    
    } };
<a name="XxBS9"></a>
### [剑指 Offer 56 - I. 数组中数字出现的次数](https://leetcode-cn.com/problems/shu-zu-zhong-shu-zi-chu-xian-de-ci-shu-lcof/)(重点)
这一题和之前写过的一题非常相似,但是那一题是一个数组里面只有一个数字出现一次,这一道题是两个数字。那道题直接所有数字异或即可得出答案。此题在哪一题的基础上应该先进行分组异或,如何保证两个组各有一个只出现一次的数字呢?

- 直接将所有数组所有元素进行异或,最终的结果就是两个只出现一次的数字以异或的结果,如果最终某一位为1就代表这两个数字的这一位不同,达成分组条件。
- 直接按照不同的哪一位来进行两种异或就可以得到最终的答案。
- **关键1, c << 1并不能改变c, c<<=1才行**
- **关键2,==的优先级要大于&,因此在与的时候应该加()**
- **注意多用()号**
```cpp
class Solution {
public:
    vector<int> singleNumbers(vector<int>& nums) {
        int a = 0;
        for(auto b : nums){
            a = a^b;
        }
        int c = 1;
        while((c&a) == 0){//注意的地方
            c = c<<1;//错误的地方
        }
        int num1 = 0, num2 = 0;
        for(auto b : nums){
            if((b&c) == 0){
                num1 = num1^b;
            } else {
                num2 = num2^b;
            }
        }
        return {num1, num2};
    }
};

剑指 Offer 56 - II. 数组中数字出现的次数 II(重点)

与上面相比,不同的是只有一个出现一次的数字,但是其他的数字出现3次。
有点难

位运算

想法很妙,但是时间复杂度比hash表高,但是空间复杂度低。
这里的想法太过巧妙,因为都是3个,只有一个出现一次。因为先统计各个位的1的数量,然后对3求余,如果不为零就代表目标数的这里也存在。

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int ans = 0;
        for (int i = 0; i < 32; ++i) {
            int total = 0;
            for (int num: nums) {
                total += ((num >> i) & 1);
            }
            if (total % 3) {
                ans |= (1 << i);
            }
        }
        return ans;
    }
};

哈希表(笨比方法)

这里我忘记了遍历hash表的方法,这里记住,将原数组遍历即可。

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        unordered_map<int, int> hash;
        for(auto a : nums){
            hash[a]++;
        }
        for(auto a : nums){
            if(hash[a] == 1){
                return a;
            }
        }
        return 0;
    }
};

剑指 Offer II 001. 整数除法(越界问题)

这一题的含金量太重了,需要考虑越界问题和事件复杂度,为n的复杂度会超时

  • 把a和b都转化成负数来计算,防止越界。
  • 这一题有TCP拥塞控制的思想,慢启动,指数级增长。
  • 复杂度为logn*logn,含金量太足了

    class Solution {
    public:
      int divide(int a, int b) {
      if (a == INT_MIN && b == -1) return INT_MAX;
    //当a为-2147483648,b为-1的时候会越界,因为整数最大只能到2147483647,因为整数首位是0,包括了0这一个非正非负的数,所以整数比负数少一位。
          int sign = (a > 0) ^ (b > 0) ? -1 : 1;//把符号存储下来
    //同上原因,把两个都转化为负数防止越界
          if (a > 0) a = -a;
          if (b > 0) b = -b;
    
          int res = 0;
          while (a <= b) {
              int value = b;
              int k = 1;
              // 0xc0000000 是十进制 -2^30 的十六进制的表示
              // 判断 value >= 0xc0000000 的原因:保证 value + value 不会溢出
              // 可以这样判断的原因是:0xc0000000 是最小值 -2^31 的一半,
              // 而 a 的值不可能比 -2^31 还要小,所以 value 不可能比 0xc0000000 小
              while (value >= 0xc0000000 && a <= value + value) {
                  value += value;
                  // 代码优化:如果 k 已经大于最大值的一半的话,那么直接返回最小值
                  // 因为这个时候 k += k 的话肯定会大于等于 2147483648 ,这个超过了题目给的范围
                  if (k > 0x3fffffff) return INT_MIN;//这里的0x3fffffff是最大正数的一半
                  k += k;
              }
              a -= value;
              res += k;
          }
    
          // bug 修复:因为不能使用乘号,所以将乘号换成三目运算符
          return sign == 1 ? res : -res;
    }
    };