刷Leetcode算法的第七天

今天是刷LT的一天,好好干饭,好好努力,好好加油哦!

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

例子

输入: [2,2,3,2]
输出: 3
输入: [0,1,0,1,0,1,99]
输出: 99

方法一:set函数
因为除了我们要找的数 其他的数都是出现了三次,
即我们可以用set函数,收集所有unique的数字,然后用下列公式即可求出我们要找的单数:
比如输入是 [a,a,a,b,b,b,c]
3 ∗ ( a + b + c ) − ( a + a + a + b + b + b + c ) = 2 c 3*(a + b + c) - (a + a+a +b +b+b+ c) = 2c 3(a+b+c)(a+a+a+b+b+b+c)=2c 再除2,即是c了

def singleNumber(nums):
	val = (3 * sum(set(nums)) - sum(nums)) // 2
	return val

方法二:位运算符
首先,理解简单的位运算符的在二进制应用。
第一: & 是当两个数都为1的时候,才为1,否则为0
例子:比如3&5 如何计算
3的二进制:0011
5的二进制:0101
则3&5的是:0001 ,即是1

3 & 5
输出:1 

第二:| 当运算符两边相同位置都是0时,结果返回0,其他情况都返回1
例子:比如3|5 如何计算
3的二进制:0011
5的二进制:0101
则3|5的是:0111,即是7

3 | 5
输出:7

第三:^ 当运算符两边相同位置都是相同数字,结果返回0,不相同时返回1
例子:比如3|5 如何计算
3的二进制:0011
5的二进制:0101
则3^5的是:0110 即是 6

3 ^ 5
输出:6 

一些 ^ 的几个特性:

  • a ^ a = 0
  • a ^ 0 = a

第四:~ 将运算符后二进制数反转,0变1,1变 0
将数字都反转
0000 0000 0000 0000 0000 0000 0000 0011 变成
1111 1111 1111 1111 1111 1111 1111 1100

应用技巧1–使用运算符来判断奇偶数

我们可以利用 & 运算符的特性,来判断二进制数第一位是0还是1
用if ((x & 1) == 0) 来判断x是否偶数 因为
1的二进制是 前面都是0只有最后一位才是 1
0000 0000 0000 0000 0000 0000 0000 0001
所以根据&的规则,必须满足两个都是0才能为1,所以x&1的前面的数必须都是0,只有最后一位,会根据x的0还是1来决定。如果x最后一位是0的话,代表x是偶数,如果x最后一位是1的话,代表是奇数。
所以当x & 1 为0的时候,x是偶数。

应用技巧2–使用运算符来取余数
如果想要计算出 x对16进行取余,那么就可以让 x & 15 得出来的结果就是余数。
15的二进制是
0000 0000 0000 0000 0000 0000 0000 1111
道理是跟上面的判断奇偶数原理一样的,仔细想想。x & 15是跟x/16一样的
但是使用 & 来进行取余的算法比使用 / 效率高很多

回到之前的Leetcode的题目
当数字出现了三次,返回0,当出现一次,返回原值。python code为:

def singleNumber(nums):
  once = twice = 0
  for num in nums:
    once = ~twice & (once ^ num)
    twice = ~once & (twice ^ num)
  return once

python解析:
当nums = [2,2,3,2]
第一次进入循环:
2 的二进制 是 0 0 1 0
而 once 和twice都是 0 0 0 0
所以 once = ~twice & (once ^ num) 中
once ^ num 仍为 num 即还是 0 0 1 0 而 ~twice 是 1 1 1 1
所以两者用&连接为 ~twice & (once ^ num) 是 0 0 1 0 因为只有两者都为1的时候才为1。 所以 once这里为 0 0 1 0

其次到 twice = ~once & (twice ^ num)中,
twice ^ num 仍为 num 0 0 1 0 ,而 ~once为 1 1 0 1
所以两者用&连接twice = ~once & (twice ^ num) 为 0 0 0 0 即twice为0

第二次进入循环:
num 还是 2 二进制为 0 0 1 0
而此时的 once为 0 0 1 0, twice为 0 0 0 0
所以 once = ~twice & (once ^ num) 中
(once ^ num) = 0 0 1 0 ^ 0 0 1 0 = 0 0 0 0 回忆 a^a = 0 的特性。
~twice 为 1 1 1 1 所以 ~twice & (once ^ num) 是 0 0 0 0 所以once为0 0 0 0

其次到 twice = ~once & (twice ^ num)中,
(twice ^ num) 仍为 num 0 0 1 0,而 ~once为 1 1 1 1
所以 ~once & (twice ^ num) 是 0 0 1 0 即twice为 0 0 1 0

第三次进入循环:
num 是 3 二进制为 0 0 1 1
而此时的 而此时的 once为 0 0 0 0, twice为 0 0 1 0
所以 once = ~twice & (once ^ num) 中
once ^ num 为 num 即 0 0 1 1 而~twice 为 1 1 0 1
所以 ~twice & (once ^ num) 是 0 0 0 1 即 once为 0 0 0 1

其次到 twice = ~once & (twice ^ num)中,
twice ^ num = 0 0 1 0 ^ 0 0 1 1 = 0 0 0 1
【回忆^是相同数字,结果返回0,不相同时返回1】
而~once为 1 1 1 0
所以~once & (twice ^ num) = 1 1 1 0 & 0 0 0 1 = 0 0 0 0 = 0 所以 twice为0

第四次进入循环:
num 是 2 二进制为 0 0 1 0
而此时的 而此时的 once为 0 0 0 1, twice为 0 0 0 0
所以 once = ~twice & (once ^ num) 中
once ^ num = 0 0 0 1 ^ 0 0 1 0 = 0 0 1 1 而 ~twice = 1 1 1 1
所以~twice & (once ^ num) = 0 0 1 1 所以once为 0 0 1 1

其次到 twice = ~once & (twice ^ num)中,
twice ^ num = num 即为 0 0 1 0 而~once = 1 1 0 0
所以~once & (twice ^ num) = 0 0 0 0 即 twice为 0

这时候 返回 once = 0 0 1 1 即是 3

这是因为当x=2出现来三次,循环三次之后 once为0 0 0 0 即不保留x=2
而x = 3的时候 循环之后 once为 x = 0 0 1 1 即为3 即可以保留x=3

使用这种方法的好处是 空间复杂度:O(1),不使用额外空间
而第一种方法虽然简单但是空间复杂度是O(N)

猜你喜欢

转载自blog.csdn.net/Jiana_Feng/article/details/115156834