其他 - 位运算 - 一个二进制数里, 有多少个 1

  1. 概述

    1. 常见的简单面试题
      1. 很多博客也讲过这道题
      2. 仅仅是自己的简单积累, 没有什么标新立异的想法
    2. 位运算
  2. 背景

    1. 大学我就没学好计算机基础和组成原理

      1. 老师太仁慈
      2. 当时自己觉得, 这些东西, 用处不大
      3. 现在想想, 还是太幼稚了
    2. 面试碰到过类似的题

      1. 新浪微博
      2. 作业帮
        1. 也是恶心
        2. 唯独作业帮终面, 问过我技术
        3. 还是 tm 位运算
        4. 还是看我一个不会, 接着又问了一个
        5. 多谢不招之恩
    3. 牢骚发完, 开始说正题

1. 问题: 一个二进制数里, 有多少个 1

  1. 概述

    1. 常见面试题
    2. 只记录思路, 不具体实现
  2. 问题

    1. 一个二进制数里, 有多少位是 1
  3. 准备

    1. 硬件

      1. 假设硬件就都能满足
        1. 范围就定在 int 里面吧
    2. 软件

      1. 语言自选
        1. 毕竟只记录思路
  4. 其他

    1. 健壮性, 输出 相关的东西, 我也不考虑

2. 思路

1. 思路1: 转化为字符类, 统计 1 的个数

  1. 概述

    1. 转化后统计
  2. 思路

    1. 转化为 String 或者 char 数组
    2. 统计 1 的个数
  3. 优劣

    1. 优势

      1. 思路简单直接
      2. 现成 api 直接使用
    2. 劣势

      1. 容易被面试官否决
        1. 他们就想考你二进制, 你用 api 把他们打发了, 会觉得有一丢丢没面子
      2. 性能有劣势
        1. 转化后遍历, 性能确实有损失
        2. 实际中, 系统代码我不清楚, 业务代码基本都是先跑通, 然后就没有然后了

2. 思路2: 位运算, 和 1 作与, 然后 右移

  1. 概述

    1. 和 思路1 类似
    2. 使用了 二进制 相关的运算
  2. 思路

    1. 结束条件

      1. 输入a 为 0
    2. 一轮循环

      1. 做与
        1. 将 输入a 和 1 作 与操作
      2. 统计
        1. 与的结果b
          1. 1: 计数加1
          2. 0: 计数不变
      3. 位移
        1. 将 输入a 右移 一位
          1. 最低位丢了就丢了吧
          2. 当然, 你也可以把 1 向左移动一位
  3. 优劣

    1. 优势

      1. 本质上还是 思路1 的延续
        1. 简单直接
        2. 可读性好
      2. 使用了 二进制, 一定程度上, 堵住了 面试官 的嘴
    2. 劣势

      1. 如果面试官还想要 骚操作, 这个操作显然不够骚
        1. 我并不觉得, 这个是 太大的劣势
      2. 性能问题
        1. 老实说, 这个思路的性能, 还有地方可以改进
        2. 比如, 我判断 10000001, 我需要走 8 轮循环

3. 思路3: 位运算, 更骚的操作

  1. 概述

    1. 另一种 位运算 的思路
  2. 思路

    1. 结束条件

      1. 输入为 0
    2. 一轮循环

      1. 减1
        1. 对 输入a 减1, 得到 b
      2. 计数
        1. 计数 加一
      3. 作与
        1. 将 a 和 b 作与
        2. 将 与 的结果, 赋值给 a, 继续下一轮
  3. 疑问: 为什么这个方法可以

    1. 问题

      1. 这种方法是如何做到题目要求的
      2. 有 理论依据 吗
    2. 结果

      1. 首先, 我无法解决 理论依据

        1. 十进制 转 二进制 的短除法, 都有 数学推理
        2. 这个我自己么想出来, 也没找到
      2. 但是可以通过 观察, 来获得相关的信息

    3. 表格

      a
      a - 1
      a & (a - 1)
      001 000 000
      010 001 000
      011 010 010
      100 011 000
      101 100 100
      110 101 100
      111 110 101
    4. 结论

      1. a = 1, a 和 (a - 1) 作与, 确实可以消掉一个 1
      2. a > 1, 高位的 1, 也可以这样消掉
      3. 一次, 只消掉 一个1
      4. 而且每次, 都只会消掉 最靠右 的那个 1
      5. 为啥是这样, 我也没搞懂...
  4. 优劣

    1. 优势
      1. 逼格高, 面试官喜欢
      2. 效率高
        1. 有几个 1, 就 只用循环几次
        2. 碰到 100000001 这种, 就会少很多轮循环
    2. 劣势
      1. 思路不那么好想
        1. 我也没想通, 我只是靠观察, 硬记住了规律
      2. 代码可读性, 没有前两个强

3. 其他

1. 与

  1. 操作

    1. 和 全0 与, 置 0
    2. 和 全1 与, 不变
  2. 应用

    1. 子网掩码
      1. 只关注我想关注的部分
      2. 其他部分直接忽视

2. 或

  1. 操作
    1. 和 全1 或, 置 1
    2. 和 全0 或, 不变

3. 非

  1. 操作
    1. 取反

4. 异或

  1. 操作

    1. 和 全1 异或, 取反
    2. 和 全0 异或, 不变
    3. 和 自己 异或, 得0
  2. 应用

    1. 面试题: 很多数, 只有一个数出现了一遍, 怎么找那个数
      1. 遍历集合, 异或 所有的数, 是一个比较简单的思路
posted @ 2020-10-14 16:13  轩辕拾銉  阅读(268)  评论(0编辑  收藏  举报