牛客面试必刷101——哈希

两数之和 BM50

原题

给出一个整型数组 numbers 和一个目标值 target,请在数组中找出两个加起来等于目标值的数的下标,返回的下标按升序排列。

(注:返回的数组下标从1开始算起,保证target一定可以由数组里面2个数字相加得到)

数据范围: 2 ≤ l e n ( n u m b e r s ) ≤ 1 0 5 , − 10 ≤ n u m b e r s i ≤ 1 0 9 , 0 ≤ t a r g e t ≤ 1 0 9 2\leq len(numbers) \leq 10^5,-10 \leq numbers_i \leq 10^9,0 \leq target \leq 10^9 2len(numbers)10510numbersi1090target109

要求:空间复杂度 O ( n ) O(n) O(n),时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)

思路:

使用哈希表来降低时间复杂度,遍历数组,如果没有 当前值, 就将(target - 当前值)存入哈希表,如果有,返回数字下标即可。

class Solution:
    def twoSum(self , numbers: List[int], target: int) -> List[int]:
        # write code here
        d = {}
        for k in range(len(numbers)):
            if numbers[k] not in d:
                d[target - numbers[k]] = k+1
            else:
                return [d[numbers[k]],k+1]
数组中出现次数超过一半的数字 BM51

原题

描述

给一个长度为 n 的数组,数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。

例如输入一个长度为9的数组[1,2,3,2,2,2,5,4,2]。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。

数据范围: n ≤ 50 n \le 50 n50,数组中元素的值 0 ≤ v a l ≤ 100000 0 \le val \le 100000 0val100000

要求:空间复杂度: O ( 1 ) O(1) O(1),时间复杂度 O ( n ) O(n) O(n)

输入描述:

保证数组输入非空,且保证有解

思路1

使用哈希表存储每个元素出现的次数,如果超过一半,直接返回该元素.

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

class Solution:
    def MoreThanHalfNum_Solution(self , numbers: List[int]) -> int:
        # write code here
        d = {}
        for k in numbers:
            if k not in d:
                d[k] = 1
            else:
                d[k] += 1
            if d[k] > len(numbers)//2:
                return k

思路2:投票法

出现次数超过一半,那该数字一定是众数,对数组中的数字进行相消,即如果两个数不相等,就消去这两个数,最坏情况下,每次消去一个众数和一个非众数,那么如果存在众数,最后留下的数肯定是众数。

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

  • a表示众数出现的次数,res存储众数。
  • 如果a=0,说明前面的数字抵消了,因此假设当前的数字为众数, r e s = k res=k res=k
  • 如果当前数字和res相等,则令a加1;否则令a减一,表示抵消
class Solution:
    def MoreThanHalfNum_Solution(self , numbers: List[int]) -> int:
        # write code here
        a = 0
        for k in numbers:
            if a == 0:
                res = k
            if res == k:
                a += 1
            else:
                a -= 1
        return res
数组中只出现一次的两个数字 BM52

原题

一个整型数组里除了两个数字只出现一次,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。

数据范围:数组长度 2 ≤ n ≤ 1000 2\le n \le 1000 2n1000,数组中每个数的大小 0 < v a l ≤ 1000000 0 < val \le 1000000 0<val1000000
要求:空间复杂度 O ( 1 ) O(1) O(1),时间复杂度 O ( n ) O(n) O(n)。提示:输出时按非降序排列。

思路1

哈希表存储每个数字出现的次数;遍历哈希表,找到只出现一次的数字

class Solution:
    def FindNumsAppearOnce(self , array: List[int]) -> List[int]:
        # write code here
        dic = {}
        lst = []
        for i in array:
            if i not in dic:
                dic[i] = 1
            else:
                dic[i] += 1
        for k in dic:
            if dic[k] < 2:
                lst.append(k)
        return sorted(lst)

思路2

异或,数组中只出现一次的数字,对数组所有元素进行异或运算,即可得到只出现一次的数字。因此解决两个只出现一次的数字,只需要将数组分为两组,两个只出现一次的数字分别分布在两组中,对两组分别进行异或运算,即可得到结果。

难点在于,如何进行分组,可以使两个元素刚好分布在两个不同的分组里。

假设要找到的两个元素为a、b,首先计算所有元素的异或,假设异或结果存储在xor中。若xor的第i位等于1,说明a和b的第i位一个是1一个是0。因此可以找到xor中的最低位1,根据最低位为0或1将原始数组分为两组即可。

  1. 首先计算所有元素的异或结果,xor
  2. 计算xor的最低位1,即令 l o w = 1 low=1 low=1,如果low&上xor等于0,说明第一位不是1,则low左移,直到low&上xor等于1,找到xor的最低位1。为了便于后续理解,假设我们找到的最低位为low_bit
  3. 元素分组,如果当前元素与上low等于0,说明该元素的第low_bit位为0;否则该元素的第low_bit位为1。这样数组元素就分为了两组,分别用a和b存储两组元素的异或结果,最终a和b即为要求的两个只出现一次的元素
class Solution:
    def FindNumsAppearOnce(self , array: List[int]) -> List[int]:
        # write code here
        xor = 0
        for k in array:
            xor ^= k
        low = 1
        while not low & xor:
            low <<= 1
        a ,b = 0,0
        for k in array:
            if low & k == 0:
                a ^= k
            else:
                b ^= k
        if a > b:
            a,b = b,a
        return [a,b]
缺失的第一个正整数 BM53

原题

给定一个未排序的整数数组nums,请你找出其中没有出现的最小的正整数 。进阶: 空间复杂度 O(1)O(1),时间复杂度 O(n)O(n)

数据范围:

− 2 31 < = n u m s [ i ] < = 2 31 − 1 -2^{31}<=nums[i]<=2^{31}-1 231<=nums[i]<=2311

0 < = l e n ( n u m s ) < = 5 ∗ 1 0 5 0<=len(nums)<=5*10^5 0<=len(nums)<=5105

思路1

使用哈希表记录nums中每个元素出现的位置,定义一个num初始化为1,当num出现在nums中,则num加1,直到nums中不存在num,直接返回num。使用哈希表存储nums中的元素,便于后续进行O(1)的查找。

class Solution:
    def minNumberDisappeared(self , nums: List[int]) -> int:
        # write code here
        d = {}
        for i in range(len(nums)):
            d[nums[i]] = i
        num = 1
        while True:
            if num in d:
                num += 1
            else:
                return num

思路2

原地哈希,一共有n个元素,则定义一个长度为n的数组,记录1-n范围内数字的出现情况。该题最终返回的要么是1-n之间的数字,要么是n+1

三数之和 BM54

原题

给出一个有n个元素的数组S,S中是否有元素a,b,c满足a+b+c=0?找出数组S中所有满足条件的三元组。

数据范围:$0 \le n \le $,数组中各个元素值满足 ∣ v a l ∣ ≤ 100 |val | \le 100 val100。空间复杂度:O(n^2)O(n2),时间复杂度 O(n^2)O(n2)

注意:

  1. 三元组(a、b、c)中的元素可以按任意顺序排列。
  2. 解集中不能包含重复的三元组。

思路

使用一个哈希表记录num中每个元素出现的位置,为了后面进行O(1)的查找

步骤:双重循环遍历num,i从0—(n-1),j从(i+1)—(n-1),计算num[i]+num[j],查找哈希表中是否存在-(num[i]+num[j]),如果存在,且该元素与num[i]和num[j]不冲突,则证明这是一个有效的三元组。

注意事项:为了避免获取到重复的结果,需要进行去重,因此先对num数组进行排序,则num[i]一定小于num[j],保证每个三元组都是有序的,因而可以直接判断当前三元组是否已经在结果集中,如果在,过滤。

class Solution:
    def threeSum(self , num: List[int]) -> List[List[int]]:
        res = []     
        d = {}
        num = sorted(num)
        for k in range(len(num)):
            d[num[k]] = k
        for i in range(len(num)):
            for j in range(i+1,len(num)):
                t = -(num[i] + num[j])
                if t in d and d[t] != i and d[t] != j:
                    if t < num[i]:
                        r = [t,num[i],num[j]]
                    elif t > num[j]:
                        r= [num[i],num[j],t]
                    else:
                        r = [num[i],t,num[j]]
                    if r not in res:
                        res.append(r)
        return res
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秃头嘤嘤魔

感谢厚爱

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值