只出现一次的数字

Posted r1-12king

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了只出现一次的数字相关的知识,希望对你有一定的参考价值。

问题

ps:部分代码参考leetcode和剑指offer。另外,三道题目都可以通过HashMap和Hashset做

1、只出现一次的数字

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

def singleNumber(nums):
    """
    异或:相同为0,不同为1
    """
    r = 0
    for i in nums:
        r ^= i
    return r

 

2、只出现一次的数字 II

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

# 方法一
def find_num_appear_once(arr):
    """
    出现3次就不能再用异或的方法了,因为三个相同的数异或还是得到本身。但是还是可以采用位运算的思想,因为出现三次的数字每个位(0或者1)也是出现三次,因此可以每一位的和能够被3整除(对3取余为0)。所以如果把每个数的二进制表示的每一位加起来,对于每一位的和,如果能被3整除,那对应那个只出现一次的数字的那一位就是0,否则对应的那一位是1。

    我们需要用一个数组bitSum保存每一位的和,具体来讲实现过程是,先初始化为0,然后对于每个数字,遍历它二进制表示的每一位,如果这一位是1,bitSum对应的那一位就加1。
    """
    if not arr:
        return

    max_length = max([len(bin(x)) for x in arr])
    bits_count = [0] * max_length
    for x in arr:
        bit_mask = 1  # 对于每个数都从最低位开始判断
        for bit_index in range(max_length - 1, -1, -1):
            if x & bit_mask != 0:
                bits_count[bit_index] += 1
            bit_mask = bit_mask << 1

    result = 0
    for count in bits_count:
        result = result << 1  # 先把当前结果左移到高位
        result += count % 3  # 然后把当前位应该为0还是1填上去

    return result


# 方法二
def find_num_appear_once_2(nums):
    seen_once = seen_twice = 0
    for num in nums:
        # first appearance:
        # add num to seen_once
        # don‘t add to seen_twice because of presence in seen_once

        # second appearance:
        # remove num from seen_once
        # add num to seen_twice

        # third appearance:
        # don‘t add to seen_once because of presence in seen_twice
        # remove num from seen_twice
        seen_once = ~seen_twice & (seen_once ^ num)
        seen_twice = ~seen_once & (seen_twice ^ num)

    return seen_once


# 方法三,对方法二的解释
def find_num_appear_once_2_explain(nums):
    one = 0
    two = 0
    for num in nums:
        # two的相应的位等于1,表示该位出现2次
        two |= (one & num)
        # one的相应的位等于1,表示该位出现1次
        one ^= num
        # three的相应的位等于1,表示该位出现3次
        three = (one & two)
        # 如果相应的位出现3次,则该位重置为0
        two &= ~three
        one &= ~three
    return one

3、只出现一次的数字 III

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

"""
从头到尾一次异或数组中的每一个数字,那么最终得到的结果就是两个只出现一次的数组的异或结果。因为其他数字都出现了两次,在异或中全部抵消了。
由于两个数字肯定不一样,那么异或的结果肯定不为0,也就是说这个结果数组的二进制表示至少有一个位为1。
我们在结果数组中找到第一个为1的位的位置,记为第n位。
现在我们以第n位是不是1为标准把元数组中的数字分成两个子数组,第一个子数组中每个数字的第n位都是1,而第二个子数组中每个数字的第n位都是0。

"""

def find_nums_appear_once(arr):
    if not arr or len(arr)<2:
        return []

    res = 0

    for i in arr:
        res = res^i

    index = find_first_bit_is_1(res)

    num1 = 0
    num2 = 0

    for i in arr:
        if is_bit_1(i,index):
            num1 = num1^i
        else:
            num2 = num2^i

    return num1,num2


def find_first_bit_is_1(num):
    """
    找到num的二进制位中最右边是1的位置
    """
    index_of_bit = 0
    while num != 0 and num & 1 == 0:
        num = num >> 1
        index_of_bit += 1
    return index_of_bit

def is_bit_1(num,index):
    """
    判断第index位是不是1
    """
    num = num>>index
    return num&1


if __name__ == "__main__":
    print(find_nums_appear_once([-8, -4, 3, 6, 3, -8, 5, 5]))

 

以上是关于只出现一次的数字的主要内容,如果未能解决你的问题,请参考以下文章

LeetCode只出现一次的数字i/ii/iii

LeetCode只出现一次的数字i/ii/iii

只出现一次的数字

只出现一次的数字

c++写算法题——只出现一次的数字

剑指Offer 数组中只出现一次的数字