剑指offer【位运算】

位运算

& 与: 两个位置都为1时才返回1(1&1=1, 1&0 =0)
| 或: 一个位置位1即可返回1 (1|0=1)
^ 异或: 两个位置,相同位0,不同为1 (1^1=0, 0^0=0, 1^0=1)
~ 取反: 1变0,0变1
<< 左移: 各二进制向左移动若干位,高位丢弃,低位补0
>> 右移: 各二进位全部右移若干位,对无符号数,高位补0

复合赋值,如 a &= b 即为 a = a&b

二进制中1的个数

逐位判断
  • 定义result = 0,用于记录1的个数
  • 使用 num & 1来判断最后一位是否为1,然后右移,遍历全部的位数
class Solution:
    def hammingWeight(self, n: int) -> int:
        res = 0
        while n:
            res += n & 1
            n >>= 1
        return res

求两数之和,不用运算符

使用位运算实现加法
  • 对于 a,b 两个数,第i位计算有以下几种形式
    1 + 1 = 0, 进位1
    1 + 0 = 1, 进位0
    0 + 1 = 1, 进位0
    0 + 0 = 0, 进位0
  • 使用c作为当前位置的计算结果,d作为进位计算结果
  • c的计算逻辑为 a ^ b, d的计算逻辑为a&b << 1 (进位1)
  • stop case d计算完了
通过abx来实现
  • 非进位和c直接通过a来存储,也就是 a^= b
  • 进位和直接存储在b上,也就是先计算x, 再 b=x
  • 最终直接return a即可
python负数的存储
  • Python 没有 int , long 等不同长度变量,即在编程时无变量位数的概念。
  • 取负数的补码: 需要将数字与十六进制数 0xffffffff 相与。可理解为舍去此数字 32 位以上的数字(将 32 位以上都变为 000 ),从无限长度变为一个 32 位整数。
  • 返回前数字还原: 若补码 aaa 为负数( 0x7fffffff 是最大的正数的补码 ),需执行 ~(a ^ x) 操作,将补码还原至 Python 的存储格式。 a ^ x 运算将 1 至 32 位按位取反; ~ 运算是将整个数字取反;因此, ~(a ^ x) 是将 32 位以上的位取反,1 至 32 位不变。
class Solution:
    def add(self, a: int, b: int) -> int:
        temp = 0xffffffff
        a, b = a & temp, b & temp
        while b != 0:
            # x = (a & b <<1) & temp # 进位和
            # a = a^b # 非进位和,直接赋值给a
            # b = x
            a, b = (a ^ b), (a & b) << 1 & temp

        if a <= 0x7fffffff: # 最大正数补码,也就是a是正数,直接输出
            return a
        else:
            return ~(a ^ temp)

数组中数字出现的次数(都出现两次,两个出现一次的数字)

如果只有一个落单的,可以直接用异或 ^ 去做

list = [a,a,b,b,....,x]

class Solution:
    def singleNumbers(self, nums: List[int]) -> List[int]:
        x = 0
        for num in nums:  # 1. 遍历 nums 执行异或运算
            x ^= num      
        return x;         # 2. 返回出现一次的数字 x
两个落单的,x,y
  • 因为相同的数字异或为0,任何数字与0异或结果是其本身。

  • 所以遍历异或整个数组最后得到的结果就是两个只出现一次的数字异或的结果:即 z = x ^ y

  • 根据异或的性质可以知道:z中至少有一位是1 (则x与y就是相等的)

  • 辅助变量m来保存z中哪一位为1.(可能有多个位都为1,我们找到最低位的1即可) e.g. z = 10 ^ 2 = 1010 ^ 0010 = 1000,第四位为1

  • 将m初始化为1,如果(z & m)的结果等于0说明z的末位为0;每次m左移,直到找到第一个z & m=1的位置,找到了首位1;

  • 首位1代表着,x,y这两个数与m做&会得到两个结果;利用这个特性把nums分成两批;再用上述的方法分别求出x,y

class Solution:
    def singleNumbers(self, nums: List[int]) -> List[int]:
        x, y, n, m = 0, 0, 0, 1
        #  遍历,求出n 用于存储 x^y的值
        for num in nums:      
            n ^= num
        # 循环左移,求出m(第一个1的位置)
        while n & m == 0:        
            m <<= 1       
        # 利用m进行分组
        # 如果和m同组,异或存到x里;
        # 如果另一组,异或存到y里
        for num in nums:        
            if num & m: x ^= num 
            else: y ^= num       
        return x, y              

数组中数字出现的次数(都出现三次,一个出现一次的数字)

数学法
  • 很简单的鸡兔同笼 假设想法
class Solution:
   def singleNumber(self, nums: List[int]) -> int:
       return (sum(set(nums))*3 - sum(nums))//2
遍历统计

对于出现三次的数字,各 二进制位 出现的次数都是 3 的倍数。
因此,统计所有数字的各二进制位中 1 的出现次数,并对 3 求余,结果则为只出现一次的数字

  • 题目给出是32位,通过左移动依次生成 0001,0010,0100...
  • 先通过一个循环来生成每一个bit的flag
  • 对于当前这一位,遍历nums,数一下1的个数
  • 如果不是3的倍数,那这个1就是落单的那个数的,用或把它加到res里
class Solution:
    def singleNumber(self, nums: List[int]) -> int:
        res = 0
        for i in range(0,32):
            bit = 1 << i  # 第i位是1,实现 ..00010.. 的向左递归
            cur_count = 0 # 用于记录当前位置的1个数
            for num in nums:
                if num & bit:
                    cur_count += 1
            # 查完了所有的数字,看一下当前位是否为3的倍数,如果不是,这个数就是res
            if cur_count % 3 != 0:
                res |= bit
        return res
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,496评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,407评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,632评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,180评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,198评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,165评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,052评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,910评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,324评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,542评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,711评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,424评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,017评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,668评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,823评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,722评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,611评论 2 353

推荐阅读更多精彩内容

  • 1.汉明距离(461-easy) 题目描述:两个整数之间的汉明距离指的是这两个数字对应二进制位不同的位置的数目。给...
    _code_x阅读 482评论 0 3
  • 1、机器数一个数在计算机中的二进制表示形式, 叫做这个数的机器数。机器数是带符号的,在计算机用一个数的最高位存放...
    世界上的一道风阅读 166评论 0 0
  • 进制基本概念 什么是进制?进制是一种计数的方式,数值的表示形式 常见的进制十进制、二进制、八进制、十六进制 进制书...
    Cc_5691阅读 3,608评论 0 3
  • 二维数组中的查找 Q: 在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按...
    菜鸡不得行阅读 3,035评论 0 2
  • 【优雅代码】05-从hashMap源码介绍位运算符 欢迎关注b站账号/公众号【六边形战士夏宁】,一个要把各项指标拉...
    要做六边形的礼洗阅读 239评论 0 0