数据结构与算法 学习笔记(8):字典、集合、哈希表
本次文章记录的是和字典、集合、哈希表等数据结构相关的LeetCode算法题(题号与LeetCode对应),包括其构造和使用,针对每一题或一类题给出了相应的代码和思路分析,并有相应的注释。
题目1:1. Two Sum
Given an array of integers, return indices of the two numbers such that they add up to a specific target.
You may assume that each input would have exactly one solution, and you may not use the same element twice.
"""
分析:求两数和,并且nums里的元素只能使用一次,说明不能用两层循环来做(就算做了时间复杂度高O(n^2))
思路:
1.在遍历每一个元素的时候,记录下它和target之间的差,保存在字典中,并且记录下当前元素位置(要返回位置)
2.当后面的元素等于前面任意一个元素和target之差(即出现在dict中),那么dict中的元素和当前位置元素之和为target,此时返回dict中元素的值域(在nums中出现的位置)和当前位置i,作为列表返回
"""
class Solution:
def twoSum(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[int]
"""
#method 1
if len(nums) <= 1:
return [-1,-1]
buff_dict = {}
for i in range(len(nums)):
if nums[i] in buff_dict:
return [buff_dict[nums[i]], i]
else:
buff_dict[target - nums[i]] = i
题目2:136. Single Number
Given a non-empty array of integers, every element appears twice except for one. Find that single one.
Note:
Your algorithm should have a linear runtime complexity. Could you implement it without using extra memory?
"""
方法1:
字典保存下nums中每个字符出现的次数(作为value),遍历字典,返回value为1对应的key,需要字典的空间
时间复杂度o(n),空间复杂度o(n)
方法2:
If we take XOR of zero and some bit, it will return that bit
a⊕0=a
If we take XOR of two same bits, it will return 0
a⊕a=0
a⊕b⊕a=(a⊕a)⊕b=b
So we can XOR all bits together to find the unique number.
时间复杂度o(n),空间复杂度o(1)
"""
class Solution(object):
def singleNumber(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
# method 1
# mapping = {}
# for i in range(len(nums)):
# if nums[i] not in mapping:
# mapping[nums[i]] = 1
# else:
# mapping[nums[i]] += 1
# for key,value in mapping.items():
# if value == 1:
# return key
res = 0
for num in nums:
res ^= num
return res
题目3:202. Happy Number
A happy number is a number defined by the following process: Starting with any positive integer, replace the number by the sum of the squares of its digits, and repeat the process until the number equals 1 (where it will stay), or it loops endlessly in a cycle which does not include 1. Those numbers for which this process ends in 1 are happy numbers.
"""
思路:
在mem集合中保存计算过程中出现过的“平方和”,
如果某次计算的平方和出现在mem中,说明开始循环了,那么直接返回False
如果出现平方和为1,说明是happy number,返回True
注:用集合来存储只出现一次的“平方和”是好方法
"""
class Solution(object):
def isHappy(self, n):
"""
:type n: int
:rtype: bool
"""
if n <= 0:
return False
mem = set()
while n != 1:
n = sum([int(i) ** 2 for i in str(n)])
if n in mem:
return False
else:
mem.add(n)
else:
return True
题目4:204. Count Primes
Count the number of prime numbers less than a non-negative number, n.
"""
思路:
prime列表把非素数对应的下标的值都置为0,素数的是1,求1的个数,就是求素数的个数
for 循环内部为 判断一个数是否为素数的方法。
"""
class Solution(object):
def countPrimes(self, n):
"""
:type n: int
:rtype: int
"""
if n < 3:
return 0
primes = [1] * n
primes[0] = primes[1] = 0
for i in range(2, int(n ** 0.5) + 1):
if primes[i] == 1:
primes[i*i: n: i] = [0] * len(primes[i*i: n: i])
return sum(primes)
题目5:205. Isomorphic Strings
Given two strings s and t, determine if they are isomorphic.
Two strings are isomorphic if the characters in s can be replaced to get t.
All occurrences of a character must be replaced with another character while preserving the order of characters. No two characters may map to the same character but a character may map to itself.
"""
方法1: 映射字符位置到d中
方法2:
0.在array中保存 每个字符的前一次出现的位置(如果第一次出现,则保存当前位置)
1.在字典中保存 每个字符的当前位置,以便下一次出现时可以读取出来,存到array中
最后,如果sarray 和 tarray一样的,说明s和t的模式相同
方法3:
将方法2做一个整合,效果相同。
三者的时间复杂度均为O(n),但方法1的空间复杂度更小一些
"""
class Solution(object):
def isIsomorphic(self, s, t):
"""
:type s: str
:type t: str
:rtype: bool
"""
"""
method 1: a mapping
"""
# d1, d2 = [0]*256, [0]*256
# for i in range(len(s)):
# if d1[ord(s[i])] != d2[ord(t[i])]:
# return False
# d1[ord(s[i])] = i+1
# d2[ord(t[i])] = i+1
# return True
"""
method 2: dict and array
"""
# n = len(s)
# ss = {}
# tt = {}
# sarray = [0]*n
# tarray = [0]*n
# for i in range(n):
# if s[i] not in ss:
# ss[s[i]] = i
# sarray[i] = i
# else:
# sarray[i] =ss[s[i]]
# ss[s[i]] = i
# for j in range(n):
# if t[j] not in tt:
# tt[t[j]] = j
# tarray[j] = j
# else:
# tarray[j] =tt[t[j]]
# tt[t[j]] = j
# return sarray == tarray
"""
method 3: dict and array, improved verision of method 2
"""
n = len(s)
ss = {}
tt = {}
sarray = [0]*n
tarray = [0]*n
for i in range(n):
if s[i] not in ss: #
ss[s[i]] = i
sarray[i] = i
elif s[i] in ss:
sarray[i] =ss[s[i]]
ss[s[i]] = i
if t[i] not in tt:
tt[t[i]] = i
tarray[i] = i
elif t[i] in tt:
tarray[i] =tt[t[i]]
tt[t[i]] = i
return sarray == tarray
题目6:217. Contains Duplicate
Given an array of integers, find if the array contains any duplicates.
Your function should return true if any value appears at least twice in the array, and it should return false if every element is distinct.
"""
method 1:
遍历列表nums,将字符保存在dic字典中,若再次出现,返回True.
method 2:
将字典改为集合,可以不用存次数,减少部分空间开销.
method 3:
return (len(nums) > len(set(nums))) 非常pythonic的做法
"""
class Solution(object):
def containsDuplicate(self, nums):
"""
:type nums: List[int]
:rtype: bool
"""
# method 1:
# n = len(nums)
# if n == 0:
# return False
# dic = {}
# for i in range(n):
# if nums[i] in dic:
# return True
# else:
# dic[nums[i]] = 1
# return False
# method 2:
# n = len(nums)
# sett = set()
# for i in range(n):
# if nums[i] in sett:
# return True
# else:
# sett.add(nums[i])
# return False
#method 3
return len(nums) > len(set(nums))
题目7:219. Contains Duplicate II
Given an array of integers and an integer k, find out whether there are two distinct indices i and j in the array such that nums[i] = nums[j] and the absolute difference between i and j is at most k.
"""
与217 Contains Duplicate 相似,只需增加距离判断。
"""
class Solution(object):
def containsNearbyDuplicate(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: bool
"""
dic = {}
for i in range(len(nums)):
if nums[i] not in dic:
dic[nums[i]] = i
else:
dic[nums[i]] = i - dic[nums[i]]
if dic[nums[i]] <= k:
return True
return False
题目8:242. Valid Anagram
Given two strings s and t , write a function to determine if t is an anagram of s.
"""
方法1:只需存在s和t中字符出现次数的字典,比较两个字典是否相等即可。
time: O(n+m)
space: O(1) 因为字符的种类是有限的,不和n相关
方法2:hashmap 构造一个hashmap存储26个字符出现的频次,存在对应的下标位置
如,a的次数存在dic[0],z的次数存在dic[25]
这样可以使得无需字典,只需一个数组,这样,比字典少存储键值。
time: O(n+m)
space: O(1)
"""
class Solution(object):
def isAnagram(self, s, t):
"""
:type s: str
:type t: str
:rtype: bool
"""
# method 1
# dic1 = {}
# dic2 = {}
# for i in range(len(s)):
# if s[i] in dic1:
# dic1[s[i]] += 1
# else:
# dic1[s[i]] = 1
# for i in range(len(t)):
# if t[i] in dic2:
# dic2[t[i]] += 1
# else:
# dic2[t[i]] = 1
# return dic1 == dic2
dic1, dic2 = [0]*26, [0]*26
for item in s:
dic1[ord(item)-ord('a')] += 1 #ord返回item的ASCII码
for item in t:
dic2[ord(item)-ord('a')] += 1
return dic1 == dic2
题目9:290. Word Pattern
Given a pattern and a string str, find if str follows the same pattern.
Here follow means a full match, such that there is a bijection between a letter in pattern and a non-empty word in str.
"""
此题与205题相似,只不过这里需要先把str中的空格删除,并把每个单词分离,形成一个列表遍历。
"""
class Solution(object):
def wordPattern(self, pattern, str):
"""
:type pattern: str
:type str: str
:rtype: bool
"""
str = str.split(' ')
dic1 = {}
dic2 = {}
array1 = [-1]*len(pattern)
array2 = [-1]*len(str)
for i in range(len(pattern)):
if pattern[i] in dic1:
array1[i] = dic1[pattern[i]]
dic1[pattern[i]] = i
else:
array1[i] = i
dic1[pattern[i]] = i
for i in range(len(str)):
if str[i] in dic2:
array2[i] = dic2[str[i]]
dic2[str[i]] = i
else:
array2[i] = i
dic2[str[i]] = i
return array1 == array2
题目10:349. Intersection of Two Arrays
Given two arrays, write a function to compute their intersection.
"""
方法1: 从nums2中找在nums1中的数字,存入res.最后将res转换成list返回
方法2: 把nums1和nums2转成set,然后求交集,但本题就是要交集,用了&的话就不合适了。
"""
class Solution(object):
def intersection(self, nums1, nums2):
"""
:type nums1: List[int]
:type nums2: List[int]
:rtype: List[int]
"""
# method 1
res = set()
nums1 =set(nums1)
for i in range(len(nums2)):
if nums2[i] in nums1:
res.add(nums2[i])
return list(res)
#method 2
#return list(set(nums1) & set(nums2))
题目11:350. Intersection of Two Arrays II
Given two arrays, write a function to compute their intersection.
"""
方法1:nums2[i]出现在nums1中时,nums2[i]加入到res,然后删去nums1中的一个nums2[i].最后返回res
方法2:
首先用一个字典counts存储nums1中数字出现的次数
nums2[i]出现在nums1中时,nums2[i]加入到res,counts对应的值减1(若等于0,就不减了).
"""
class Solution(object):
def intersect(self, nums1, nums2):
"""
:type nums1: List[int]
:type nums2: List[int]
:rtype: List[int]
"""
# method 1
# res = []
# for i in range(len(nums2)):
# if nums2[i] in nums1:
# res.append(nums2[i])
# nums1.remove(nums2[i])
# return res
# method 2
counts = {}
res = []
for num in nums1:
counts[num] = counts.get(num, 0) + 1
for num in nums2:
if num in counts and counts[num] > 0:
res.append(num)
counts[num] -= 1
return res
题目12:387. First Unique Character in a String
Given a string, find the first non-repeating character in it and return it’s index. If it doesn’t exist, return -1.
"""
目标:找出未重复的元素+第一个未重复的 所以可分解为两步做
1.找出未重复的元素和其下标 遍历字符串
用字典,字典存下每个字符的出现的位置,下标i;
如果在后面的字符中又出现这个字符,则令其值为-1
2.第二步就是找出字典中不等于-1的且最小的数,这个数就是满足目标的数
3.如果字符串有不重复的数,则返回其下标;反之返回-1
"""
class Solution(object):
def firstUniqChar(self, s):
"""
:type s: str
:rtype: int
"""
mapping = {}
ans = len(s)
temp = ans
for i, c in enumerate(s):
if c in mapping:
mapping[c] = -1
else:
mapping[c] = i
for c in mapping:
if mapping[c] != -1:
if mapping[c]<temp:
temp = mapping[c]
if temp!=ans:
return temp
else:
return -1
题目13:389. Find the Difference
Given two strings s and t which consist of only lowercase letters.
String t is generated by random shuffling string s and then add one more letter at a random position.
Find the letter that was added in t.
"""
方法1:先统计s中的字符频次保存在dic中,t中的字符若在dic中,对应频次减1(直至0),若不在直接返回其。
方法2: 参考LeetCode 136题(single number)
0.s 和 t只相差一个字符,那么s + t 中,有一个单个的字符(其余都成对出现)。
1.让其元素先与"0"异或,得到首字符,然后依次与后面的异或,根据异或的结合律等,最后的结果是单出来的那个字符。
注:异或应在ASCII码域进行,完成后再转成字符形式返回
"""
class Solution(object):
def findTheDifference(self, s, t):
"""
:type s: str
:type t: str
:rtype: str
"""
# method 1: dict
# dic = {}
# for ch in s:
# dic[ch] = dic.get(ch,0) + 1
# for c in t:
# if c in dic and dic[c] > 0:
# dic[c] -= 1
# else:
# return c
# method 2: XOR
code = 0
for ch in s + t:
code ^= ord(ch)
return chr(code)
题目14:409. Longest Palindrome
Given a string which consists of lowercase or uppercase letters, find the length of the longest palindromes that can be built with those letters.
This is case sensitive, for example “Aa” is not considered a palindrome here.
Note:
Assume the length of given string will not exceed 1,010.
"""
分析:
0. Sum代表能构成的回文最大长度,flag表示是否出现过频次为奇数的字符
1. 首先是将s中的字符频次统计。
2.遍历dic,如果当前字符频次为偶数,那么Sum和其频次相加;
3.如果是奇数,Sum和其减1后的频次相加(奇数只有偶数部分的能构成回文)并设flag为1(因为单出来的可以放在回文中间)
4.最后返回Sum +flag 就行
时间复杂度 O(n)
"""
class Solution(object):
def longestPalindrome(self, s):
"""
:type s: str
:rtype: int
"""
Sum = 0
flag = 0
dic = {}
for ch in s:
dic[ch] = dic.get(ch,0) + 1
for c in dic:
if dic[c]%2 == 0:
Sum += dic[c]
else:
flag = 1
Sum += dic[c] -1
return Sum + flag #flag代表统计的字符中有频次是奇数次的。
题目15:575. Distribute Candies
Given an integer array with even length, where different numbers in this array represent different kinds of candies. Each number means one candy of the corresponding kind. You need to distribute these candies equally in number to brother and sister. Return the maximum number of kinds of candies the sister could gain.
"""
0.sister尽可能多分,但要平均数量,所以最大为len(candies)//2;
1.sister想多分种类的话,必须得多拿单个的,最大就是每个种类拿一遍(set(candies))
3.取以上两个的小的那个,满足要求。
"""
class Solution(object):
def distributeCandies(self, candies):
"""
:type candies: List[int]
:rtype: int
"""
#method 1
return min(len(candies) // 2, len(set(candies)))
题目16:594. Longest Harmonious Subsequence
We define a harmonious array is an array where the difference between its maximum value and its minimum value is exactly 1.
Now, given an integer array, you need to find the length of its longest harmonious subsequence among all its possible subsequences.
"""
要求:求值相差为1的最长序列
思路:
如果我们把nums中每个数字出现的次数,保存在字典中
那么把键值作为检索的标记,那么相邻的两个检索(键值差1的检索)的值域的和反应的是和谐序列,要求最长和谐序列,便是求最大的(相邻检索的值域的和)。
注:求最大可以放在循环里保存更新,不需要全部求出后再求最大值,时间开销更少。
时间复杂度:构造字典O(n),求相邻键值和O(n),总共O(n)
"""
class Solution(object):
def findLHS(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
res=0
mp={}
for i in nums:
if i not in mp:
mp[i] = 1
else:
mp[i] += 1
for ch in mp:
#mp.get(),返回指定键的值,如果键不存在,返回None
# ch+1 保证不溢出,如果是ch,可能导致下一句中mp[ch+1]不存在,而出错
if mp.get(ch+1,None):
res=max(res,mp[ch] + mp[ch+1]) #相邻两个数字的次数和
return res
题目17:1002. Find Common Characters
Given an array A of strings made only from lowercase letters, return a list of all characters that show up in all strings within the list (including duplicates). For example, if a character occurs 3 times in all strings but not 4 times, you need to include that character three times in the final answer.
"""
方法1:利用python内置容器Counter类(字典的子类)
Counter(x)能返回x的每个元素出现的次数
此题思路:
0.由A[0]创建Conter对象res,统计A[0]中每个字符出现的次数
1.随后遍历A列表中的每个str,也用Counter对象去保存,然后与res求交集.
2.返回时将Counter对象res转换为列表。
方法2和方法1思想是一样的,实现思路一样,但形式更加底层。
"""
class Solution(object):
def commonChars(self, A):
"""
:type A: List[str]
:rtype: List[str]
"""
# # method 1: pythonic
# res = collections.Counter(A[0])
# for a in A:
# res &= collections.Counter(a)
# return list(res.elements())
# method 2: not build-in
answer = None
for a in A:
# create the counts for the current word
work = {}
for c in a:
if c in work:
work[c] += 1
else:
work[c] = 1
# compare the current word counts
# keep the least of the common ones
# delete the ones in the answer not found in the current word
if answer != None:
keys = list(answer.keys())
for k in keys:
if k in work:
answer[k] = min(answer[k], work[k])
else:
del answer[k]
else:
answer = work
# turn the counts into an array
answerArr = []
for key in answer:
count = answer[key]
for _ in xrange(count):
answerArr.append(key)
return answerArr
本次记录就到这为止~
说明
本文章所有代码及文档均以上传至github中,感谢您的rp and star.
github链接:https://github.com/LSayhi
点击阅读原文:https://github.com/LSayhi/Algorithms
CSDN链接:https://blog.csdn.net/LSayhi
微信公众号:AI有点可ai