python时间复杂度和空间复杂度_利用Python位运算简化时间/空间复杂度

利用位运算简化算法题中的时间/空间复杂度

最近两天碰到两个用位运算解决的题目,恰好一个利用位运算简化了时间复杂度,另一个简化了空间复杂度,所以做个记录加深一下印象。

第一个题目是LeetCode 421. Maximum XOR of Two Numbers in an Array:

**注:这题中提到的异或均是按位异或。

Given a non-empty array of numbers, a0, a1, a2, … , an-1, where 0 ≤ ai < 231.

Find the maximum result of ai XOR aj, where 0 ≤ i, j < n.

Could you do this in O(n) runtime?

Example:

Input: [3, 10, 5, 25, 2, 8]

Output: 28

Explanation: The maximum result is 5 ^ 25 = 28.

这题的描述中明确指出了do this in O(n) runtime ,如果要两两异或比较大小的话很明显需要O(n^2)的时间复杂度。

那怎么样才能用位运算解决这个问题呢?

如果我们遇到的场景是这样:

假定数组中所有的元素都可以用8位2进制数来表示,如果我们已经知道所有数前7位的最大异或值maxor,怎样求8位数的最大异或值?

很容易想到8位数的最大异或值只可能是前7位后面跟上0或1, 所以我们先假设后面能跟上1,即假设的最大异或值为

maxornew =(maxor << 1)+ 1;

我们要找到这个最大异或的值会不会出现,一种简单的办法是2个循环遍历数组,然后两两算出异或的值,看是否能找到一个结果与maxornew相同。但是这样会使时间复杂度变成O(n^2),不满足题目的要求。

但是按位异或有一个比较特殊的性质:

若 x ^ y = z ,那么 x ^ z = y , y ^ z = x

这样我们只用拿出数组中每一个八位二进制数, 与maxornew进行异或运算,再判断异或得到的结果是否在数组中,如果不在的话,最大的异或值就是(maxor<<1) ,这样时间复杂度就被简化成为了O(n)

上面的说完了,这个题目中描述的问题也就迎刃而解了。题目中提到的numbers,由于在给出的函数声明中传入参数为List[int], LeetCode中这样的数一般认为是 32位的int。

对numbers中每一个数取前n位(1<=n<=32)计算最大异或的值,并且按照上面计算第八位的方法计算下一位,就可以得到整个数组的最大异或值。

1

2

3

4

5

6

7

8def (self, nums):

answer = 0

for i in range(32)[::-1]:

answer <<= 1

prefixes = {num >> i for num in nums}

answer += any(answer^1 ^ p in prefixes for p in prefixes)

return answer

这题利用位运算将原本需要O(n^2)的时间复杂度简化成了O(n)

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

第二个题目是简化空间复杂度,LeetCode 137. Single Number II:

Given an array of integers, every element appears three times except for one, which appears exactly once. Find that single one.

Note:

Your algorithm should have a linear runtime complexity. Could you implement it without using extra memory?

就是说一个数组中,除了其中一个数只出现一次,其他都出现了三次,找出这个只出现了一次的数。

首先我想到的是一个时间复杂度为O(n),使用的额外空间也是O(n)的解法:

1

2

3

4

5

6

7

8

9

10

11

12

13

14def singleNumber(self, nums):

"""

:type nums: List[int]

:rtype: int

"""

numdict = {}

for i in nums:

if i in numdict:

numdict[i]+=1

else:

numdict[i]=1

for i in nums:

if numdict[i]==1:

return i

简单来说就是遍历数组的同时记录每个数出现的次数,第二次遍历的时候找出出现了一次的数。

如果要将空间复杂度简化成O(1), 使用位运算是一个很好的选择:

将所有数转换成32位的2进制表示

把每一位的值加起来对3取余

转换成十进制表示

Python将负整数转换成二进制数的时候,直接是’-‘加上这个数的绝对值的二进制表示,所以负号单独计算。

代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17class Solution(object):

def singleNumber(self, nums):

"""

:type nums: List[int]

:rtype: int

"""

sumlist = [0]*32

summary, negcnt = 0, 0

for i in nums:

negcnt += i < 0

i = abs(i)

bitwise = format(i, '032b')

for j in xrange(32):

sumlist[j] += int(bitwise[j])

for i in xrange(32):

summary += 2**(31-i)*(sumlist[i]%3)

return summary*[1,-1][negcnt%3]

和上面的方法比起来,额外空间简化成了常数, 即O(1)。

不过话说回来,这是一种用时间换空间的做法,上面的做法时间是2n, 这个是32n,虽然复杂度都是O(n),但是实际运行时间差距还是比较大的。

( · x · )~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值