先写个哈希表的定义:散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。也是刚刚百度得到的。
第771题:宝石与石头(简单)哈希表
给定字符串J
代表石头中宝石的类型,和字符串 S
代表你拥有的石头。 S
中每个字符代表了一种你拥有的石头的类型,你想知道你拥有的石头中有多少是宝石。
J
中的字母不重复,J
和 S
中的所有字符都是字母。字母区分大小写,因此"a"
和"A"
是不同类型的石头。
示例 1:
输入: J = "aA", S = "aAAbbbb"
输出: 3
示例 2:
输入: J = "z", S = "ZZ"
输出: 0
注意:
S
和J
最多含有50个字母。-
J
中的字符不重复。
J就是我们要查询的表,而S就是一个要处理的字符串,所以我们就是要将S中的每一个字符遍历一遍,看是否在J中出现,具体代码如下:
class Solution:
def numJewelsInStones(self, J, S):
"""
:type J: str
:type S: str
:rtype: int
"""
count = 0
for i in S:
if i in J:
count =count + 1
return count
811:子域名访问计数(简单)哈希表
一个网站域名,如”discuss.leetcode.com”,包含了多个子域名。作为顶级域名,常用的有”com”,下一级则有”leetcode.com”,最低的一级为”discuss.leetcode.com”。当我们访问域名”discuss.leetcode.com”时,也同时访问了其父域名”leetcode.com”以及顶级域名 “com”。
给定一个带访问次数和域名的组合,要求分别计算每个域名被访问的次数。其格式为访问次数+空格+地址,例如:”9001 discuss.leetcode.com”。
接下来会给出一组访问次数和域名组合的列表cpdomains
。要求解析出所有域名的访问次数,输出格式和输入格式相同,不限定先后顺序。
示例 1:
输入:
["9001 discuss.leetcode.com"]
输出:
["9001 discuss.leetcode.com", "9001 leetcode.com", "9001 com"]
说明:
例子中仅包含一个网站域名:"discuss.leetcode.com"。按照前文假设,子域名"leetcode.com"和"com"都会被访问,所以它们都被访问了9001次。
示例 2
输入:
["900 google.mail.com", "50 yahoo.com", "1 intel.mail.com", "5 wiki.org"]
输出:
["901 mail.com","50 yahoo.com","900 google.mail.com","5 wiki.org","5 org","1 intel.mail.com","951 com"]
说明:
按照假设,会访问"google.mail.com" 900次,"yahoo.com" 50次,"intel.mail.com" 1次,"wiki.org" 5次。
而对于父域名,会访问"mail.com" 900+1 = 901次,"com" 900 + 50 + 1 = 951次,和 "org" 5 次。
注意事项:
-
cpdomains
的长度小于100
。 - 每个域名的长度小于
100
。 - 每个域名地址包含一个或两个”.”符号。
- 输入中任意一个域名的访问次数都小于
10000
。
这道题目逻辑上是非常简单的,就是建立一个字典,字典的key就是这些域名,值就是每一个域名被访问的次数,对输入的list循环,每进来一个string就进行相应的处理,把对应的域名和子域名传入到字典中,全部整合完毕后,就将字典再转化为列表,具体代码如下:
class Solution:
def subdomainVisits(self, cpdomains):
"""
:type cpdomains: List[str]
:rtype: List[str]
"""
dict = {}
result = []
for s in cpdomains:
L = s.split(' ')
a = int(L[0])
l = L[1].split('.')
if dict. __contains__(l[-1]):
dict[l[-1]] = dict[l[-1]] + a
else:
dict[l[-1]] = a
if dict. __contains__(l[-2]+'.'+l[-1]):
dict[l[-2]+'.'+l[-1]] = dict[l[-2]+'.'+l[-1]] + a
else:
dict[l[-2]+'.'+l[-1]] = a
if(len(l) == 3):
if dict. __contains__(l[-3]+'.'+l[-2]+'.'+l[-1]):
dict[l[-3]+'.'+l[-2]+'.'+l[-1]] = dict[l[-3]+'.'+l[-2]+'.'+l[-1]] + a
else:
dict[l[-3]+'.'+l[-2]+'.'+l[-1]] = a
for i in dict.items():
result.append(str(i[1])+' '+i[0])
return result
虽然思想很简单,但是我这个代码确实写的有点挫。
这是别人的代码:
class Solution:
def subdomainVisits(self, cpdomains):
c = collections.Counter()
for cd in cpdomains:
n, d = cd.split()
c[d] += int(n)
for i in range(len(d)):
if d[i] == '.': c[d[i + 1:]] += int(n)
return ["%d %s" % (c[k], k) for k in c]
这里它用了python里面的collections.Counter()这个类方法,具体可以参照python标准库,这个标准库的类方法用来做累加计数确实特别方便,确实在算法设计上有很多值得借鉴的地方。
500:键盘行(简单)哈希表
给定一个单词列表,只返回可以使用在键盘同一行的字母打印出来的单词。键盘如下图所示。
示例:
输入: ["Hello", "Alaska", "Dad", "Peace"]
输出: ["Alaska", "Dad"]
注意:
- 你可以重复使用键盘上同一字符。
- 你可以假设输入的字符串将只包含字母。
对于这一题,我们可以首先建立三个列表,分别表示键盘的123行;,然后将键盘的信息存储到这个里面,然后对于每一个输入进来的字符串,判断是否是属于同一个str,这里通过转化为列表,然后用集合的方式去处理,具体代码如下:
class Solution:
def findWords(self, words):
"""
:type words: List[str]
:rtype: List[str]
"""
l1 = list("QWERTYUIOPqwertyuiop")
l2 = list("ASDFGHJKLasdfghjkl")
l3 = list("ZXCVBNMzxcvbnm")
result = []
for s in words:
l0 = list(s)
if(set(l0).issubset(set(l1)) or set(l0).issubset(set(l2)) or set(l0).issubset(set(l3))):
result.append(s)
return result
575:分糖果(简单)哈希表
给定一个偶数长度的数组,其中不同的数字代表着不同种类的糖果,每一个数字代表一个糖果。你需要把这些糖果平均分给一个弟弟和一个妹妹。返回妹妹可以获得的最大糖果的种类数。
示例 1:
输入: candies = [1,1,2,2,3,3]
输出: 3
解析: 一共有三种种类的糖果,每一种都有两个。
最优分配方案:妹妹获得[1,2,3],弟弟也获得[1,2,3]。这样使妹妹获得糖果的种类数最多。
示例 2 :
输入: candies = [1,1,2,3]
输出: 2
解析: 妹妹获得糖果[2,3],弟弟获得糖果[1,1],妹妹有两种不同的糖果,弟弟只有一种。这样使得妹妹可以获得的糖果种类数最多。
这道题目比较简单,具体代码如下,看代码应该就能理解,主要就是把列表转化为集合这个想法:
class Solution:
def distributeCandies(self, candies):
"""
:type candies: List[int]
:rtype: int
"""
s = set(candies)
if(len(s)<len(candies)/2):
return len(s)
else:
return len(candies)//2
811:有效的字母异位词(简单)哈希表 排序
给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的一个字母异位词。
示例 1:
输入: s = "anagram", t = "nagaram"
输出: true
示例 2:
输入: s = "rat", t = "car"
输出: false
说明:
你可以假设字符串只包含小写字母。
进阶:
如果输入字符串包含 unicode 字符怎么办?你能否调整你的解法来应对这种情况?
对于这一题,要求是t是否是s的字母异位词,很明显的是,如果s是t的字母异位词,那么我们就有s和t的排序应该是一模一样的,所以我们对s和t进行排序,然后去判断这两个是否一样就可以了,具体的代码如下:
class Solution:
def isAnagram(self, s, t):
"""
:type s: str
:type t: str
:rtype: bool
"""
if(len(s)!=len(t)):
return False
l0 = list(s)
l0.sort()
l1 = list(t)
l1.sort()
for i in range(len(l0)):
if(l0[i]!=l1[i]):
return False
return True
这个是通过排序的方法去做,排序的话时间复杂度是O(nlogn),接下来我们要用一个时间复杂度更低的算法,用hash table来做。
思想其实很简单就是去计数,总共我们知道有26个字母,那么建立一个字典,对应于26个字母,然后遍历字符串,去算每一个字母出现的次数,最后两个字典如果相等就返回true。具体代码如下:
def isAnagram1(self, s, t):
dic1, dic2 = {}, {}
for item in s:
dic1[item] = dic1.get(item, 0) + 1
for item in t:
dic2[item] = dic2.get(item, 0) + 1
return dic1 == dic2
def isAnagram2(self, s, t):
dic1, dic2 = [0]*26, [0]*26
for item in s:
dic1[ord(item)-ord('a')] += 1
for item in t:
dic2[ord(item)-ord('a')] += 1
return dic1 == dic2
这是别人写的两个方法,第一个里面有个dict.get(item,0)这个方法就是有就返回具体值,没有就是0,第二个方法里面就是ord便是字符串的位置信息,都是python里面的一些语言技巧。第二个方法里面生成的是一个包含26个0元素的列表。
811:字符串中的第一个唯一字符(简单)哈希表 字符串
给定一个字符串,找到它的第一个不重复的字符,并返回它的索引。如果不存在,则返回 -1。
案例:
s = "leetcode"
返回 0.
s = "loveleetcode",
返回 2.
注意事项:您可以假定该字符串只包含小写字母。
这道题其实我自己都没有想到什么好的方法,一开始,所以看到别人的一个代码如下:
class Solution:
def firstUniqChar(self, s):
"""
:type s: str
:rtype: int
"""
letters='abcdefghijklmnopqrstuvwxyz'
index=[s.index(l) for l in letters if s.count(l) == 1]
print(index)
return min(index) if len(index) > 0 else -1
这个代码中,利用了字符串的两个方法,一个是count,还有一个是index,分别是去数字符出现的个数以及出现的位置,同时也利用了正则表达式去简化代码,这两个类方法运用到这个题目当中是非常好的,也使得思想更加的明朗,就是对26个字母看每个在字符串中出现了几次,出现一次的话就返回位置,最后把位置最小的给输出就好了。当然论坛上还有一些其它的方法,但是整体上都是利用了python语言的相关特性,从算法本身角度看,都是用count去做,并没有什么太大新意,或者用字典去做。
599:两个列表的最小索引总和(简单)哈希表
假设Andy和Doris想在晚餐时选择一家餐厅,并且他们都有一个表示最喜爱餐厅的列表,每个餐厅的名字用字符串表示。
你需要帮助他们用最少的索引和找出他们共同喜爱的餐厅。 如果答案不止一个,则输出所有答案并且不考虑顺序。 你可以假设总是存在一个答案。
示例 1:
输入:
["Shogun", "Tapioca Express", "Burger King", "KFC"]
["Piatti", "The Grill at Torrey Pines", "Hungry Hunter Steakhouse", "Shogun"]
输出: ["Shogun"]
解释: 他们唯一共同喜爱的餐厅是“Shogun”。
示例 2:
输入:
["Shogun", "Tapioca Express", "Burger King", "KFC"]
["KFC", "Shogun", "Burger King"]
输出: ["Shogun"]
解释: 他们共同喜爱且具有最小索引和的餐厅是“Shogun”,它有最小的索引和1(0+1)。
提示:
- 两个列表的长度范围都在 [1, 1000]内。
- 两个列表中的字符串的长度将在[1,30]的范围内。
- 下标从0开始,到列表的长度减1。
- 两个列表都没有重复的元素。
这一道题目,首先我的想法就是如果答案只有一个的话,那么我们就可以在寻找最小的索引的同时并且寻找共同喜爱的餐厅,就是对list1进行迭代,那么我们从list2中找一样的,然后看是否这一次的索引和更小,更小的话就替换掉喜爱的餐厅,这是很多题目的的套路,但是这里由于可能有多个答案,所以我的想法就是分开做,先去找最小的索引,把索引找出来以后,再去判断在这样的索引和的情况下,到底有多少组答案,具体的代码如下:
class Solution:
def findRestaurant(self, list1, list2):
"""
:type list1: List[str]
:type list2: List[str]
:rtype: List[str]
"""
minIndex = 2000
result = []
for i in range(len(list1)):
try:
a = list2.index(list1[i])
except BaseException:
continue
else:
if(i + a<minIndex):
minIndex = i + a
print(minIndex)
for j in range(minIndex+1):
if(j<len(list1) and minIndex-j<len(list2) and list1[j] == list2[minIndex-j]):
result.append(list1[j])
return result
以上是我自己在解题的思路,下面我们去看看答案以及别人的想法。答案上的第一个方法和我们的思路一样,都是通过表的查找,用hash map的思想去做,它的第二个方法就特别的有意思,它是从索引和的角度出发,分别取计算索引和为0,1….一旦出现不为空的情况就返回结果,代码如下:
class Solution:
def findRestaurant(self, list1, list2):
"""
:type list1: List[str]
:type list2: List[str]
:rtype: List[str]
"""
for i in range(len(list1)+len(list2)-1):
result = []
for j in range(i+1):
if(j<len(list1) and i-j<len(list2) and list1[j] == list2[i-j]):
result.append(list1[j])
if(len(result)>0):
return result
这个代码是比较慢的,因为没有用hash map,两个for循环的情况下,时间复杂度是更加的高的。
594: 最长和谐子序列 (简单) 哈希表
和谐数组是指一个数组里元素的最大值和最小值之间的差别正好是1。
现在,给定一个整数数组,你需要在所有可能的子序列中找到最长的和谐子序列的长度。
示例 1:
输入: [1,3,2,2,5,2,3,7]
输出: 5
原因: 最长的和谐数组是:[3,2,2,2,3].
说明: 输入的数组长度最大不超过20,000。
这道题目的话最暴力的解法,复杂度是O(2^n),就是把所有的子序列遍历一遍,然后找出其中最长的和谐数组,很显然这个方法一定会超时。还有一种优化后的暴力算法,也是我一开始想到的,就是对于数组里面的每一个数,将其个数以及比它大1的个数给数出来,然后选出最大的那种,具体代码如下,这个很简单:
class Solution:
def findLHS(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
result = 0
s = set(nums)
for i in s:
a = nums.count(i)
b = nums.count(i+1)
if(a+b>result and b!=0):
result = a+b
return result
尽管这个算法的复杂度已经降了很多,是O(n^2),不过还是会超时。接下来我们用hash map来做,首先建立一个hash table,具体代码如下:
class Solution:
def findLHS(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
dict0 = {}
result = 0
for i in range(len(nums)):
dict0[nums[i]] = dict0.get(nums[i], 0) + 1
for key in dict0:
a = dict0.get(key, 0)
b = dict0.get(key+1, 0)
if(a+b>result and dict0.get(key+1, 0)!= 0 ):
result = a+b
return result
其实思想上是跟上面是一样的,但是由于利用了hash map明显降低了时间复杂度,所以这个是可以通过的。上面我们用的是双循环,其实单循环也是可以做到的,单循环就是每来一个数,我们就更新一下最后的结果,具体的代码如下:
class Solution:
def findLHS(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
dict0 = {}
result = 0
for i in range(len(nums)):
dict0[nums[i]] = dict0.get(nums[i], 0) + 1
a = dict0.get(nums[i], 0)
b = dict0.get(nums[i]+1, 0)
if(a+b>result and dict0.get(nums[i]+1, 0)!= 0 ):
result = a+b
a = dict0.get(nums[i], 0)
b = dict0.get(nums[i]-1, 0)
if(a+b>result and dict0.get(nums[i]-1, 0)!= 0 ):
result = a+b
return result
代码写的不是很好,但是具体的思想大概就是这样。
最后还有一个用sort的方法去做的,就是先把数组排玩顺序之后再去做分析。其实在面对很多需要去比较数的大小的时候,我们先进行排序是一个很好的策略。
290.:单词模式 (简单)哈希表
给定一种 pattern(模式)
和一个字符串 str
,判断 str
是否遵循相同的模式。
这里的遵循指完全匹配,例如, pattern
里的每个字母和字符串 str
中的每个非空单词之间存在着双向连接的对应模式。
示例1:
输入: pattern = "abba", str = "dog cat cat dog"
输出: true
示例 2:
输入:pattern = "abba", str = "dog cat cat fish"
输出: false
示例 3:
输入: pattern = "aaaa", str = "dog cat cat dog"
输出: false
示例 4:
输入: pattern = "abba", str = "dog dog dog dog"
输出: false
说明:
你可以假设 pattern
只包含小写字母, str
包含了由单个空格分隔的小写字母。
关于这一题,我个人的思想就是把两个字符串映射到一个用数字表示的字典中去,abba就对应于1221,而我们建立的词典就是{‘a’:1,’b’:2},所以说我就先贴上代码:
class Solution:
def wordPattern(self, pattern, str):
"""
:type pattern: str
:type str: str
:rtype: bool
"""
l1 = list(pattern)
l2 = str.split(" ")
dict1 = {}
dict2 = {}
count1 = 1
count2 = 1
if(len(l1)!=len(l2)):
return False
for i in range(len(l1)):
if(dict1. __contains__(l1[i])):
l1[i] = dict1[l1[i]]
else:
dict1[l1[i]] = count1
l1[i] = count1
count1 = count1 + 1
if(dict2. __contains__(l2[i])):
l2[i] = dict2[l2[i]]
else:
dict2[l2[i]] = count2
l2[i] = count2
count2 = count2 + 1
if(l1[i]!=l2[i]):
return False
return True
代码写的比较冗余,因为其实是对两个字符串进行的相同的操作,只不过在中间进行比较。
204:计数质数(简单)哈希表 数学
统计所有小于非负整数 n 的质数的数量。
示例:
输入: 10
输出: 4
解释: 小于 10 的质数一共有 4 个, 它们是 2, 3, 5, 7
这道题目有明显的暴力解法,那就是对于每一个数,我们判断它是不是质数,如果是的话,那就记一次,那么这个的时间复杂度是O(n^2),除了暴力解法之外,论坛上有一个如下的解法:
class Solution:
def countPrimes(self, n):
"""
:type n: int
:rtype: int
"""
if n < 3:
return 0
primes = [True] * n
primes[0] = primes[1] = False
for i in range(2, int(n ** 0.5) + 1):
if primes[i]:
primes[i * i: n: i] = [False] * len(primes[i * i: n: i])
return sum(primes)
他是首先认为所有的数都是质数,然后依次将2*[2,3,4…],3*[3,4,5…]…等这些全部置成合数,然后最终得到结果,这里利用了列表的赋值,整体的复杂度就是O(n),要比暴力解决快很多。