[leetcode刷题笔记]数学与位运算

位运算是二进制中比较常见的运算,包括按位与&,按位或|,非~,异或∧ 等,本文记录LeetCode刷题一些知识点,水平有限还望多多指正

1.只出现一次的数字

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

根据异或运算的三个特点:

  • 1.交换律:a∧ b∧ c=a∧ c∧ b
  • 2.任何数于0异或为任何数: 0 ∧ n = n
  • 3.相同的数异或为0: n ∧ n = 0

若输入[4,1,2,1,2],则4∧1∧2∧1∧2=4∧(1∧1)∧(2∧2)=4∧0∧0=4即为所求

class Solution:
    def singleNumber(self, nums: List[int]) -> int:
        res = 0
        for i in nums:
            res^=i
        return res

2.只出现一次的数字II

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

在上一题中,除了某元素外其他元素均出现两次,消除规则为0 -> 1 -> 0,因此使用一个二进制位即可。在本题中其他数字出现三次,需要两个二进制位完成 00 -> 01 -> 10 -> 00。如何使某位在0与1之间转换,可以根据以下公式。

x \& \sim x=0 \\ x \& \sim 0=x \

设b,a代表两个二进制位,当出现3次是,其他元素归零,最后仅剩b,a = 0,1,即a此时位元素值。

class Solution:
    def singleNumber(self, nums: List[int]) -> int:
        a, b = 0, 0
        for num in nums:
            a = a ^ num & ~b
            b = b ^ num & ~a
        return a

3.只出现一次的数字III

给定一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。

首先通过异或运算能够求出这两个数字的异或diff,可以用 diff作为标记来分离 xy

如何求数字x,考虑异或结果的某个非 0 位如最后一个非 0 位, 因为我们知道只有当 xy在该位不一样的时候才会出现异或结果为 1,我们通过 diff \& (-diff)保留diff最右边的 1,这个 1 要么来自x,要么来自y,所以我们以该位是否为 1 对数组进行划分, 只要该位为 1 就和 x 异或, 只要该位为 0就和 y异或, 这样最终得到就是只出现过一次的两个数。

下面清楚的说明diff \& (-diff)的作用,因为 -diff = ~diff + 1,故:

diff \& (-diff) = diff \& (~diff + 1)

class Solution:
    def singleNumber(self, nums: List[int]) -> int:
        diff, x,y = 0,0,0
        for i in nums:
            diff ^=i
        diff&=(~ diff+1) 
        for i in nums:
            if diff&i==0:
                x ^=i
            else:
                y ^=i
        return x,y

4.缺失数字

给定一个包含 0, 1, 2, ..., n 中 n 个数的序列,找出 0 .. n 中没有出现在序列中的那个数。

若输入[0,1,3,4],其中i为下标,num为数值,将其异或运算,该题就变成了数组中只有一个数字出现一次,如表

i 0 1 2 3
num 0 1 3 4

可以将结果的初始值设为n,再对数组中的每一个数以及它的下标进行一个异或运算,即:

res=4∧(0∧0)∧(1∧1)∧(2∧3)∧(3∧4)\\=(4∧4)∧(0∧0)∧(1∧1)∧(3∧3)∧2\\ =0∧0∧0∧0∧2\\ =2
即为所求

class Solution:
    def missingNumber(self, nums: List[int]) -> int:
        res = len(nums)
        for i,num in enumerate(nums):
            res ^= i^num
        return res

5.消失的两个数字

给定一个数组,包含从 1 到 N 所有的整数,但其中缺了两个数字。你能在 O(N) 时间内只用 O(1) 的空间找到它们吗?

将问题转换为nums数组的所有数和1~N的数一同构成的集合特征为——该集合内只有2个数字出现过1次,而其余数字都出现2次,请找出这2个只出现1次的数字。

class Solution:
    def missingTwo(self, nums: List[int]) -> List[int]:
        diff, x,y = 0,0,0
        N = len(nums) + 2
        for i in range(1, N+1):
            diff ^= i
        for i in nums:
            diff ^= i

        diff&=~ diff 

        for i in nums:
            if i & diff:
                x ^= i
            else:
                y ^= i

        for i in range(1, N+1):
            if i & diff:
                x ^= i
            else:
                y ^= i

        return [x, y]

6.不用加减乘除做加法

写一个函数,求两个整数之和,要求在函数体内不得使用 “+”、“-”、“*”、“/” 四则运算符号。

∧异或 ----相当于 无进位的求和, 想象10进制下的模拟情况:(如:19+1=20;无进位求和就是10,而非20;因为它不管进位情况)

& 与 ----相当于求每位的进位数, 先看定义:1\&1=11\&0=00\&0=0;即都为1的时候才为1,正好可以模拟进位数的情况,还是想象10进制下模拟情况:(9+1=10,如果是用&的思路来处理,则9+1得到的进位数为1,而不是10,所以要用<<1向左再移动一位,这样就变为10了);

这样公式就是:(a \wedge b) \wedge((a \& b)<<1) 即:每次无进位求 + 每次得到的进位数--------我们需要不断重复这个过程,直到进位数为0为止;

class Solution:
    def add(self, a: int, b: int) -> int:
        while b: 
            a, b = (a ^ b) & 0xFFFFFFFF, ((a & b) << 1) & 0xFFFFFFFF
        return a if a <= 0x7FFFFFFF else ~ a ^ 0xFFFFFFFF

[注]为什么要加 0xFFFFFFFF,在leetcode解析上,作者jyd给出了如下解释。
由于 Python 的数字存储特点,需要做一些特殊处理,以下详细介绍。
Python 负数的存储:
Python / Java 中的数字都是以 补码 形式存储的。但 Python 没有 int , long 等不同长度变量,即没有变量位数的概念。
获取负数的补码: 需要将数字与十六进制数 0xffffffff 相与。可理解为舍去此数字 32 位以上的数字,从无限长度变为一个 32位整数。
返回前数字还原: 若补码 a 为负数(0x7fffffff 是最大的正数的补码 ),需执行~(a ^ x)操作,将补码还原至 Python 的存储格式。a ^ x运算将 1至 32 位按位取反; ~ 运算是将整个数字取反;因此,~(a ^ x) 是将 32 位以上的位取反,即由 0 变为 1, 1 至 32 位不变。

print(hex(1)) # = 0x1 补码
print(hex(-1)) # = -0x1 负号 + 原码 ( Python 特色,Java 会直接输出补码)

print(hex(1 & 0xffffffff)) # = 0x1 正数补码
print(hex(-1 & 0xffffffff)) # = 0xffffffff 负数补码

print(-1 & 0xffffffff) # = 4294967295 ( Python 将其认为正数)

7.数组中数字出现的次数

一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。

class Solution:
    def singleNumbers(self, nums: List[int]) -> List[int]:
        ret=0
        for num in nums:
            ret ^= num
        div = 1
        while div & ret == 0:
            div <<= 1
        a, b = 0, 0
        for num in nums:
            if num & div:
                a ^= num
            else:
                b ^= num
        return [a, b]

8.3的幂

给定一个整数,写一个函数来判断它是否是 3 的幂次方。你能不使用循环或者递归来完成本题吗?

使用log,若n为3的幂次方,则\log _{3}(n)为整数,根据换底公式

n=3^{i}\\ i=\log _{3}(n) =\frac{\log _{b}(n)}{\log _{b}(3)}

判断是否为整数只需判断i%1是否为0即可

class Solution:
    def isPowerOfThree(self, n: int) -> bool:
        from math import log10
    
        if n <= 0:
            return False
    
        return (log10(n)/log10(3))%1 == 0

再次注意,由于精度问题,在此使用log10()而非log()

9.2的幂

给定一个整数,编写一个函数来判断它是否是 2 的幂次方。

可以使用上题求对数,也可根据二进制的性质,若n为2的幂,则n\&(n-1) = 0

class Solution:
    def isPowerOfTwo(self, n: int) -> bool:

        if n < 1:
            return False

        return n&(n-1) == 0

题目和部分解析来源力扣(LeetCode),更多内容后续补充,欢迎指正。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,509评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,806评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,875评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,441评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,488评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,365评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,190评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,062评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,500评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,706评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,834评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,559评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,167评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,779评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,912评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,958评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,779评论 2 354