牛客网在线编程专题《剑指offer-面试题40》数组中只出现一次的数字

我的个人微信公众号:Microstrong

微信公众号ID:MicrostrongAI

微信公众号介绍:Microstrong(小强)同学主要研究机器学习、深度学习、计算机视觉、智能对话系统相关内容,分享在学习过程中的读书笔记!期待您的关注,欢迎一起学习交流进步!

知乎主页:https://www.zhihu.com/people/MicrostrongAI/activities

Github:https://github.com/Microstrong0305

个人博客:https://blog.csdn.net/program_developer

 题目链接:

https://www.nowcoder.com/practice/e02fdb54d7524710a7d664d082bb7811?tpId=13&tqId=11193&tPage=2&rp=2&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking

题目描述:

 解题思路:

(1)以空间复杂度换时间复杂度

空间复杂度O(n),时间复杂度O(n)

已经AC的代码:

# -*- coding:utf-8 -*-
class Solution:
    # 返回[a,b] 其中ab是出现一次的两个数字
    def FindNumsAppearOnce(self, array):
        # write code here
        num_dic = {}
        result_list = []
        for item in array:
            if item in num_dic.keys():
                num_dic[item] = num_dic[item] + 1
            else:
                num_dic[item] = 1

        for item, count in num_dic.items():
            if count == 1:
                result_list.append(item)

        return result_list


if __name__ == "__main__":
    input_array = [2, 4, 3, 6, 3, 2, 5, 5]
    sol = Solution()
    print(sol.FindNumsAppearOnce(input_array))

(2)利用Python中list.count()函数,时间复杂度为o(n)

# -*- coding:utf-8 -*-
class Solution:
    # 返回[a,b] 其中ab是出现一次的两个数字
    def FindNumsAppearOnce(self, array):
        # write code here
        result_list = []
        for item in array:
            count = array.count(item)
            if count == 1:
                result_list.append(item)

        return result_list

(3)运用异或思想,时间复杂度为O(n),空间复杂度为O(1)

异或概念:如果a、b两个值不相同,则异或结果为1。如果a、b两个值相同,异或结果为0。

        这是一个比较难的题目, 很少有人能在面试的时候不需要提示一下子想到最好的解决办法。一般当应聘者想了几分钟后还没有思路, 面试官会给出一些提示。面试官很有可能会说: 你可以先考虑这个数组中只有一个数字只出现一次, 其他的都出现了两次, 怎么找出这个数字?

        这两个题目都在强调一个(或两个〉数字只出现一次, 其他的出现两次。 这有什么意义呢?我们想到异或运算的一个性质:任何一个数字异或它自己都等于0。也就是说,如果我们从头到尾依次异或数组中的每一个数字, 那么最终的结果刚好是那个只出现一次的数字, 因为那些成对出现两次的数字全部在异或中抵消了。

        想明白怎么解决这个简单问题之后, 我们再回到原始的问题, 看看能不能运用相同的思路。 我们试着把原数组分成两个子数组, 使得每个子数组包含一个只出现一次的数字, 而其他数字都成对出现两次。 如果能够这样拆分成两个数组, 我们就可以按照前面的办法分别找出两个只出现一次的数字了。

         我们还是从头到尾依次异或数组中的每一个数字, 那么最终得到的结果就是两个只出现一次的数字的异或结果。 因为其他数字都出现了两次,在异或中全部抵消了。由于这两个数字肯定不一样, 那么异或的结果肯定不为0,也就是说在这个结果数字的二进制表示中至少就有一位为1。我们在结果数字中找到第一个为1 的位的位置, 记为第n位。 现在我们以第n位是不是1为标准把原数组中的数字分成两个子数组,第一个子数组中每个数字的第n位都是1,而第二个子数组中每个数字的第n位都是0。 由于我们分组的标准是数字中的某一位是 1 还是 0,那么出现了两次的数字肯定被分配到同一个子数组。 因为两个相同的数字的任意一 位都是相同的,我们不可能把两个相同的数字分配到两个子数组中去, 于是我们已经把原数组分成了两个子数组,每个子数组都包含一个只出现一次的数字,而其他数字都出现了两次。我们已经知道如何在数组中找出唯一一个只出现一次数字,因此到此为止所有的问题都已经解决了。

        举个例子,假设输入数组{ 2, 4, 3, 6, 3, 2, 5, 5}。 当我们依次对数组中的每一个数字做异或运算之后, 得到的结果用二进制表示是 0010。 异或得到结果中的倒数第二位是1,于是我们根据数字的倒数第二位是不是1分为两个数组。第一个子数组{2, 3, 6, 3, 2}中所有数字的倒数第二位都是1,而第二个子数组{4, 5, 5}中所有数字的倒数第二位都是0。接下来只要分别对这两个子数组求异或,就能找出第一个子数组中只出现一次的数字是 6,而第二个子数组中只出现一次的数字是4。

        想清楚整个过程之后再写代码就不难了。 下面是已经AC的代码:

# -*- coding:utf-8 -*-
class Solution:

    def FindNumsAppearOnce(self, array):
        # write code here
        if array == None or len(array) < 2:
            return []

        # 对数据进行异或运算
        resultExclusiveOR = 0
        for i in array:
            resultExclusiveOR ^= i

        # 在异或结果中找出从右向左第一个为1的索引
        indexOf1 = self.FindFirstBitIs1(resultExclusiveOR)

        num1 = 0
        num2 = 0
        for j in array:
            if self.IsBit1(j, indexOf1):
                num1 ^= j
            else:
                num2 ^= j

        return [num1, num2]

    def FindFirstBitIs1(self, num):
        indexBit = 0
        while num & 1 == 0:
            num = num >> 1
            indexBit += 1
        return indexBit

    def IsBit1(self, num, indexBit):
        num = num >> indexBit
        return (num & 1)


if __name__ == "__main__":
    input_array = [2, 4, 3, 6, 3, 2, 5, 5]
    sol = Solution()
    print(sol.FindNumsAppearOnce(input_array))

Reference:

【1】剑指Offer,何海涛著。

【2】剑指offer-数组中只出现过一次的数字-Java版,地址:https://www.bilibili.com/video/av88622216?from=search&seid=17735582839041767037     

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值