python输出一个数组中只出现一次得数字_(python版)《剑指Offer》JZ40:数组中只出现一次的数字...

#【只出现一次的数系列】位运算

本题和主站 260 是一样的. 除了这个,主站还有 136 和 137,645。 总共加起来本系列一共 四道题。

四道题全部都是位运算的套路,如果你想练习位运算的话,不要错过~

作者:fe-lucifer

链接:https://leetcode-cn.com/problems/shu-zu-zhong-shu-zi-chu-xian-de-ci-shu-lcof/solution/zhi-chu-xian-yi-ci-de-shu-xi-lie-wei-yun-suan-by-a/

来源:力扣(LeetCode)

基础知识

①. 异或a^b

将 a 和 b 的二进制每一位进行运算,得出的数字。 运算的逻辑是同一位的数字相同则为 0,不同则为 1

②.异或运算

重点~ 切记!

x ^ 0 = x​ , x ^ 1 = ~x

任何数和 本身异或则为 0

任何数和 0 异或 是 本身

异或 满足交换律。a ^ b ^ c ,等价于 a ^ c ^ b

③.与运算

重点~ 切记!

x & 0 = 0 , x & 1 = x

1.LC136[链接]

d9ede6086504b1e6bd8e8dea0859baa2.png

我们执行一次全员异或即可

class Solution:

def singleNumber(self, nums: List[int]) -> int:

single_number = 0

for num in nums:

single_number ^= num

return single_number

时间复杂度:O(N),其中N为数组长度。

空间复杂度:O(1)

====================================

2.LC137[链接]

a96da6e92717ba9dc8df6256451c39d2.png

【思路1】

将输入数组存储到 HashSet,然后使用 HashSet 中数字和的三倍与数组之和比较。

3 x (a + b + c) - (a + a + a + b + b + b + c) = 2 c

class Solution:

def singleNumber(self, nums: List[int]) -> int:

return (3 * sum(set(nums)) - sum(nums)) // 2

时间复杂度:O(N),遍历输入数组。

空间复杂度:O(N),存储 N/3 个元素的集合。

【思路2】有限状态自动机

对于所有数字中的某二进制位 11 的个数,存在 3 种状态,即对 3 余数为 0, 1, 20,1,2 。

若输入二进制位 11 ,则状态按照以下顺序转换;

若输入二进制位 00 ,则状态不变。

0→1→2→0→⋯

77c275547c95bbeffda3ccbedd8dd46d.png

由于二进制只能表示 0, 1。因此需要使用两个二进制位来表示 3 个状态。设此两位分别为 two , one,则状态转换变为:

00→01→10→00→⋯

7048c316a52b42982e56fe55c0e4cf43.png

bfd2fa473cdc3b225bf2e4e73ce7f84e.png

num是数组的每个值,不一定是1,0

emm ~ ~

作者是拿0 1数写的特例,对大于1的数,我就很懵了~ 有点绕

求指点 ~

class Solution:

def singleNumber(self, nums: List[int]) -> int:

ones, twos = 0, 0

for num in nums:

ones = ones ^ num & ~twos

twos = twos ^ num & ~ones

return ones

'''

作者:jyd

链接:https://leetcode-cn.com/problems/single-number-ii/solution/single-number-ii-mo-ni-san-jin-zhi-fa-by-jin407891/

来源:力扣(LeetCode)

'''

【思路3】位操作

我们单独看二进制某一位,先不看单独的那个数,其他所有数字都出现了 3 次,所以那一位是 1 的个数一定是 3 的倍数。

如[2,2,3,2] ==》[10,10,11,10],其中10(2)有三个,则1有3个. 模3为0

再考虑这个出现一次的数,如果这一位是 1 ,那么这一位 1 的次数模 3 为 1 ,否则的话模 3 就是 0 。

其中11(2)有1个,则1有1个. 模3为1

那么就很简单了,统计一下 这一位上 有多少个数 是 1 ,然后模 3 取余数,结果就是这个单独的数这一位上的值了。

遍历 32 位整数的每一位,就可以得到这个单独的数是多少了。

如果其他数都出现了 k 次,一个数出现了一次。那么:

如果 k 是偶数,还是把所有的数异或起来就行了。

如果 k 是奇数,那么统计每一位是 1 的个数,然后模 k 取余数就能得到那个单独的数了。

d54b8d66fc3ee4af9e98eab0a2a4faf2.png

上述解释的作者:godweiyang

链接:https://leetcode-cn.com/problems/single-number-ii/solution/zi-dong-ji-wei-yun-suan-zui-xiang-xi-de-tui-dao-gu/

class Solution:

def singleNumber(self, nums: List[int]) -> int:

counts = [0] * 32

for num in nums:

# 统计每个位上的 1的个数

for j in range(32):

counts[j] += num & 1

num >>= 1

res, m = 0, 3

for i in range(32):

# i从0开始,表示count从最后一位开始取模

res <<= 1

# res为0时,左移也无效,所以它针对的是有值的时候

res |= counts[31 - i] % m # 得到 模3为1的数

return res if counts[31] % m == 0 else ~(res ^ 0xffffffff)

'''

作者:jyd

链接:https://leetcode-cn.com/problems/single-number-ii/solution/single-number-ii-mo-ni-san-jin-zhi-fa-by-jin407891/

来源:力扣(LeetCode)

'''

注:

res |= counts[31 - i] % m # 得到 模3为1的数,别担心,它不会得到2,不用发愁它得到2怎么办,题目设定的是 除了一次,只有三次,即res只会得到 模3为0/1

return res if counts[31] % m == 0 else ~(res^0xffffffff) # count[31]即左第一列,若为0,则res为正数;

若为负,则res为负,要进行取反操作

eaea5b35890f94d109b144d6193c2ca7.png

【附:思路3打印版】

class Solution2:

def singleNumber(self, nums):

counts = [0] * 32

for num in nums:

print('num = ',num,end='\t')

# 二进制数的打印

if num>=0:

print(bin(num)[2:].zfill(32)) # 正数的原码

else:

print(bin(num & 0xffffffff)[2:])# 负数的补码

# 统计每个位上的 1的个数

for j in range(32):

# count是倒着存,第一位存最右边一位的 1的个数

counts[j] += num & 1 # 更新第 j 位

num >>= 1 # 第 j 位 --> 第 j + 1 位

# 比如2的二进制10,num先是0,右移

print('counts = ',counts)

res, m = 0, 3

for i in range(32):

res <<= 1

print('i = %d,原res=%d'%(i,res))

print('counts[%d] = %d'%(31 - i,counts[31 - i]))

# i从0开始,表示count从最后一位开始取模

res |= counts[31 - i] % m # 得到 模3为1的数

print('后res',res)

print()

return res if counts[31] % m == 0 else ~(res ^ 0xffffffff)

s = Solution2()

nums = [-2,-2,-3,-3,-3,5,-2]

one = s.singleNumber(nums)

print('只出现一次的是:',one)

打印个负数的看看

bece35006d2463fd92b804d6c82b2174.png

【附:思路3的简洁版】

这里的cnt在每次循环都会清空,记录每一位的1个数后,立即判断 模3的结果,不等0(即为1)就进行 或操作

class Solution:

def singleNumber(self, nums: List[int]) -> int:

res = 0

for i in range(32):

cnt = 0 # 记录当前 bit 有多少个1

bit = 1 << i # 记录当前要操作的 bit

for num in nums:

if num & bit != 0:

cnt += 1

if cnt % 3 != 0:

# 不等于0说明唯一出现的数字在这个 bit 上是1

res |= bit

return res - 2 ** 32 if res > 2 ** 31 - 1 else res

'''

作者:fe-lucifer

链接:https://leetcode-cn.com/problems/shu-zu-zhong-shu-zi-chu-xian-de-ci-shu-lcof/solution/zhi-chu-xian-yi-ci-de-shu-xi-lie-wei-yun-suan-by-a/

来源:力扣(LeetCode)

'''

【补充知识点】

计算机内存中一律用补码来表示整数:正数的补码 就等于它的原码,而负数的补码 按位取反再+1

对于一个负数 -x,它的二进制表示(补码)求法如下:1. 求出-x 的 相反数x 的原码 (-x=-10, x=10,x的原码:0000 1010)

2. 对 x 的原码进行取反运算 (取反:1111 0101)

3. 将取反运算的结果+1 (+1 :1111 0110)

# https://blog.csdn.net/u012511672/article/details/51724272

二进制数的打印nums=[1,-1,2,-2,3,-3,4,-4,5,-5,6,-6,7,-7]

for num in nums:

if num>=0:

print(num,'\t',bin(num)[2:].zfill(32))

else:

print(num,'\t',bin(num & 0xffffffff)[2:])

f08491b1eda0d2f441fad6a07ced5fcd.png

运算符优先级【从低到高】 参考链接

运算符优先级 【从低到高】

Lambda (运算优先级最低)

逻辑运算符: or

逻辑运算符: and

逻辑运算符:not

成员测试: in, not in

同一性测试: is, is not

比较: ,>=,!=,==

按位或: |。只有 0|0为0,其他情况为1

按位异或: ^。相同为0,相异为1

按位与: &。只有 1 &1 为1,其他情况为0。

移位: 左移<< , 右移>>(2的幂相关)

加法与减法: + ,-

乘法、除法与取余: *, / ,%

正负号: +x,-x (运算优先级最高)

惭愧~ 这道题啃了好久,都没太搞懂。关键是遗忘了位运算知识,回忆起来 到 灵活运用,是一个漫长的过程

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值