LeetCode之团灭异或问题

一、不用额外变量交换两个数个数

def swapNumbers(self, numbers: List[int]) -> List[int]:
    numbers[0] ^= numbers[1]
    numbers[1] ^= numbers[0]
    numbers[0] ^= numbers[1]
  return numbers

二、136. 只出现一次的数字

一个数组中有一种数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这种数

class xor():
    # 一个数组中有一种数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这种数
    def xor(self, arr):
        n = len(arr)
        eor = 0
        for i in range(n):
            eor ^= arr[i]
        return eor

 三、怎样把一个int类型的数,提取出最右侧的1来 

假设n
则结果为 n&((~n)+1)


四、137. 只出现一次的数字 II

给你一个整数数组 nums ,除某个元素仅出现 一次 外,其余每个元素都恰出现 三次 。请你找出并返回那个只出现了一次的元素。

你必须设计并实现线性时间复杂度的算法且不使用额外空间来解决此问题。

LeetCode之团灭异或问题_第1张图片

建立一个长度为 32 的数组 counts,通过以上方法可记录所有数字的各二进制位的 111出现次数。

int[] counts = new int[32];
for(int i = 0; i < nums.length; i++) {
    for(int j = 0; j < 32; j++) {
        counts[j] += nums[i] & 1; // 更新第 j 位
        nums[i] >>>= 1; // 第 j 位 --> 第 j + 1 位
    }
}

将 counts各元素对 3求余,则结果为 “只出现一次的数字” 的各二进制位。

for(int i = 0; i < 32; i++) {
    counts[i] %= 3; // 得到 只出现一次的数字 的第 (31 - i) 位 
}

利用 左移操作 和 或运算 ,可将 counts数组中各二进位的值恢复到数字 res 上(循环区间是 i∈[0,31])。

for(int i = 0; i < counts.length; i++) {
    res <<= 1; // 左移 1 位
    res |= counts[31 - i]; // 恢复第 i 位的值到 res
}

代码实现 

class Solution:
    def singleNumber(self, nums: List[int]) -> int:
        counts = [0] * 32
        for num in nums:
            for j in range(32):
                counts[j] += num & 1
                num >>= 1
        res, m = 0, 3
        for i in range(32):
            res <<= 1
            res |= counts[31 - i] % m
        return res if counts[31] % m == 0 else ~(res ^ 0xffffffff)

五、260. 只出现一次的数字 III

给你一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。你可以按 任意顺序 返回答案。

你必须设计并实现线性时间复杂度的算法且仅使用常量额外空间来解决此问题。

解题思路

假设数组中这两种数为a和b,那么我对数组全部进行异或运算,得到的eor=a ^ b,一定如此。又因为a和b是两种数,那么eor=(a ^ b) != 0。那么eor一定存在某一位上不是0而是1。假设eor的第八位为1,那么我们可以将整个数组分为两个部分,即第八位上=0和第八位上=1的,并且a和b 一定是分属于两个部分的。此时,我想只异或第八位上=1的那部分,用eor’表示,那么eor’一定是a或者b,具体是谁不知道,假如是a那么如何去求b呢??那就是b= eor^eor’ (b= a ^ b ^  a)。这里第八位为1只是一个例子,那么找哪个呢??就找最右侧为1,根据3来求,知道了最右侧的1,就可以根据推理过程求出问题。

def singleNumber(self, arr: List[int]) -> List[int]:
        n = len(arr)
        eor = 0
        for i in range(n):
            eor ^= arr[i]
        # eor = a ^ b
        # eor != 0
        # eor 必然有一个位置上是1
        rightone = eor & (~eor + 1) # 提取最右侧为1
        onlyone = 0
        for i in range(n):
            if rightone & arr[i] != 0:
                onlyone ^= arr[i]
        otherone = onlyone ^ eor
        return onlyone, otherone

六、输出二进制1的个数

# 1.暴力法,遍历一个一个的数,for
# 2.用异或
class xor():
    # 输出二进制1的个数
    def xor(self, n):
        count = 0
        while n != 0:
            rightone = n & ((~n)+1)  # 每次先提取最右侧的1
            count += 1  # 提取一个计数器加一个
            n ^= rightone  # 提取完就将最右侧的1抹掉
        return count

参考文章

异或问题总结_zeronose的博客-CSDN博客

LeetCode2588之统计美丽子数组数目(相关话题:前缀异或和)_击水三千里的博客-CSDN博客

你可能感兴趣的:(算法,leetcode,算法)