zoukankan      html  css  js  c++  java
  • 位运算

    add(a,b)

    不用+-*/运算符   实现add(a,b)

    题目来源:https://leetcode-cn.com/problems/bu-yong-jia-jian-cheng-chu-zuo-jia-fa-lcof

    C++:

        int add(int a, int b) {
            //循环终止条件  没有进位了为止
            while (b != 0) {
                //保存进位和
                int c = (unsigned int)(a & b) << 1; //c++中,负数不支持左移,把符号位给移了怎么办?
                //保存无进位和
                a = a ^ b;
                //如果还有进位 c!=0,再循环,如果没有,输出无进位和a 
                b = c;
            }
            return a;
        }

    Python:

    def add(self, a: int, b: int) -> int:
        """
        # 写法1 递归
        if b==0: return a
        return add(a^b,(a&b)<<1);
        """
        # 2 循环 
        x = 0xffffffff
        a,b = a&x,b&x
        while b!=0:
            a,b = a^b, (a&b)<<1 &x
        return a if a<=0x7fffffff else ~(a^x)
    • 为什么a,b = a&x,b&x?  
      取补码
    • 为什么a<=0x7ffffffff返回~(a^x)? 
      负数的补码a-> 还原为->负数本身~(a^x)

    负数在python中的存储问题(python可能会将负数的补码看成正数,所以用到时候需要还原负数)

    • 正数的补码即本身
    • 负数的补码在反码的基础上+1

    Python,Java 等语言中的数字都是以 补码 形式存储的。 Python 没有 int , long 等不同长度变量,即在编程时无变量位数的概念

    • 获取负数的补码: 需要将数字与十六进制数 0xffffffff 相与。可理解为舍去此数字 32 位以上的数字(将 32 位以上都变为 00 ),从无限长度变为一个 32 位整数。
    • 返回前数字还原: 若补码 a 为负数( 0x7fffffff 是最大的正数的补码 2147483647  即 01111111111111111111111111111111),需执行 ~(a ^ x) 操作,将补码还原至 Python 的存储格式。
      a ^ x 运算将 1 至 32 位按位取反; ~ 运算是将整个数字取反;因此, ~(a ^ x) 是将 32 位以上的位取反,1 至 32 位不变。

    例子:python的负数补码情况_hxshine的博客-CSDN博客_python 负数补码

    vector<int> singleNumbers(vector<int>& nums)

    输入数组是【a,a,b,b,c,x,c,d,d,e,e,f,x,f,g,g,....】中有两个数是single其它是两个,要求O(N) O(1)算法输出数组中只出现一次的那两个数

    源自leetcode题目:https://leetcode-cn.com/problems/shu-zu-zhong-shu-zi-chu-xian-de-ci-shu-lcof/

    python:

    def singleNumbers(self, nums: List[int]) -> List[int]:
        x,y,xorSum,suppBitOfxOry = 0,0,0,1
        # 计算x xor y
        for num in nums:
            xorSum ^=num
        # 找到任意一个supp(x xor y)
        while not xorSum & suppBitOfxOry:
            suppBitOfxOry = suppBitOfxOry << 1
        # 用一个bit位将nums分成两个集合 【x,a,a,b,b,c,c,d,d,...】 【y,e,e,f,f,g,g,h,h,...】  两个相同的数一定在同一个集合中 
        # 这两个只出现一次的数字x,y与m做"与运算"时,一个结果会是0,一个非0  根据这个性质做区分   
        # 至于其他的数同样可以被这个bit分为两部分,而且任意一对相同的两个数被分到了一起 不管它匹配了x还是y都无所谓,因为最后会被异或给消去
        for num in nums:
            if num & suppBitOfxOry:
                # 【x,a,a,b,b,c,c,d,d,...】 根据异或性质只会留下x 
                x^=num
            else:
                y^=num
        return x,y

    c++:

    vector<int> singleNumbers(vector<int>& nums) {
        int xorSum = 0;
        int x=0,y=0;
        int oneSuppBitOfxOry = 1;
        for(int i=0;i<nums.size();i++){
            xorSum ^=nums[i];
        }
        while((xorSum & oneSuppBitOfxOry)==0) oneSuppBitOfxOry=oneSuppBitOfxOry<<1;//注意这里的运算符优先级  写成while(xorSum & oneSuppBitOfxOry==0) 出大问题
        for(int i=0;i<nums.size();i++){
            if((nums[i]&oneSuppBitOfxOry)==0){
                x^=nums[i];
            }else{
                y^=nums[i];
            }
        }
        vector<int> res;
        res.push_back(x);
        res.push_back(y);
        return res;
    
    }

    P.S. 运算符优先级(copy 百度百科)

    优先级
    运算符
    名称或含义
    使用形式
    结合方向
    说明
    1
    []
    数组下标
    数组名[常量表达式]
    左到右
     
    ()
    圆括号
    (表达式)/函数名(形参表)
     
    .
    成员选择(对象)
    对象.成员名
     
    ->
    成员选择(指针)
    对象指针->成员名
     
    2
    -
    负号运算符
    -表达式
    右到左
    单目运算符
    (类型)
    强制类型转换
    (数据类型)表达式
     
    ++
    前置自增运算符
    ++变量名
    单目运算符
    ++
    后置自增运算符
    变量名++
    单目运算符
    --
    前置自减运算符
    --变量名
    单目运算符
    --
    后置自减运算符
    变量名--
    单目运算符 [4] 
    *
    取值运算符
    *指针变量
    单目运算符
    &
    取地址运算符
    &变量名
    单目运算符
    !
    逻辑非运算符
    !表达式
    单目运算符
    ~
    按位取反运算符
    ~表达式
    单目运算符
    sizeof
    长度运算符
    sizeof(表达式)
     
    3
    /
    表达式/表达式
    左到右
    双目运算符
    *
    表达式*表达式
    双目运算符
    %
    余数(取模)
    整型表达式/整型表达式
    双目运算符
    4
    +
    表达式+表达式
    左到右
    双目运算符
    -
    表达式-表达式
    双目运算符
    5
     
    左移
    变量
    左到右
    双目运算符
    >>
    右移
    变量>>表达式
    双目运算符
    6
    >
    大于
    表达式>表达式
    左到右
    双目运算符
    >=
    大于等于
    表达式>=表达式
    双目运算符
     
    小于
    表达式
    双目运算符
     
    小于等于
    表达式
    双目运算符
    7
    ==
    等于
    表达式==表达式
    左到右
    双目运算符
    !=
    不等于
    表达式!= 表达式
    双目运算符
    8
    &
    按位与
    表达式&表达式
    左到右
    双目运算符
    9
    ^
    按位异或
    表达式^表达式
    左到右
    双目运算符
    10
    |
    按位或
    表达式|表达式
    左到右
    双目运算符
    11
    &&
    逻辑与
    表达式&&表达式
    左到右
    双目运算符
    12
    ||
    逻辑或
    表达式||表达式
    左到右
    双目运算符
    13
    ?:
    条件运算符
    表达式1? 表达式2: 表达式3
    右到左
    三目运算符
    14
    =
    赋值运算符
    变量=表达式
    右到左
     
    /=
    除后赋值
    变量/=表达式
     
    *=
    乘后赋值
    变量*=表达式
     
    %=
    取模后赋值
    变量%=表达式
     
    +=
    加后赋值
    变量+=表达式
     
    -=
    减后赋值
    变量-=表达式
     
     
    左移后赋值
    变量
     
    >>=
    右移后赋值
    变量>>=表达式
     
    &=
    按位与后赋值
    变量&=表达式
     
    ^=
    按位异或后赋值
    变量^=表达式
     
    |=
    按位或后赋值
    变量|=表达式
     
    15
    ,
    逗号运算符
    表达式,表达式,…
    左到右
    从左向右顺序运算
    说明:
    同一优先级的运算符,运算次序由结合方向所决定。
    简单记就是:! > 算术运算符 > 关系运算符 > && > || > 赋值运算符

     int singleNumber(vector<int>& nums)

    输入数组是【aaabbbcccdddexeefffgggiii,....】中有1个数是single其它是triple,输出数组中只出现一次的那个数

    题目来源leetcode,K神题解:面试题56 - II. 数组中数字出现的次数 II(位运算 + 有限状态自动机,清晰图解) - 数组中数字出现的次数 II - 力扣(LeetCode) (leetcode-cn.com)

    python:

    def singleNumber(self, nums: List[int]) -> int:
        # hash表  谁都会写  面试有啥用? 面试一写就offer挂啦
    
        # 位运算 + 有限状态自动机  什么脑洞 AAAAAAAAAAAAAAAAAAWWWWWWWWWWWWWWWWWWWWWWW
        # 对于最低位而言 这个位上有多少个1记为wt(b_0)  wt(b_0) mod 3 in {0,1,2}    三种状态     00 01 10 分别对应表示0,1,2  
        # 直接应用在所有的位上逻辑同样成立
        a,b = 0,0
        for num in nums:
            a = ~b &(num^a)
            # 状态高位是在状态地位修改后的基础上修改的
            b = ~a &(num^b)
        # 因为状态的缘故  只关心状态啊=的地位就可以  就是说当所有的数加上之后,状态的高位肯定是0
        return a

    更应该关注的是状态如何转移 和 如何简化代码!

    本题的技巧:观察真值表利用位运算符来替代条件判断语句

  • 相关阅读:
    golang的缓冲channel简单使用
    golang协程同步的几种方法
    红黑树原理详解及golang实现
    go路由httprouter中的压缩字典树算法图解及c++实现
    golang编译源代码和交叉编译方法
    cmake使用笔记
    如何用redis设计数据库初探
    muduo学习笔记(六) 多线程的TcpServer
    利用 Blob 处理 node 层返回的二进制文件流字符串并下载文件
    数据量庞大的分页穿梭框实现
  • 原文地址:https://www.cnblogs.com/PiaYie/p/15420065.html
Copyright © 2011-2022 走看看