查找表
一、考虑的基本数据结构
1、第一类: 查找有无–set
元素’a’是否存在,通常用set:集合
set只存储键,而不需要对应其相应的值。
set中的键不允许重复
2、第二类: 查找对应关系(键值对应)–dict
元素’a’出现了几次:dict–>字典
dict中的键不允许重复
3、第三类: 改变映射关系–map
通过将原有序列的关系映射统一表示为其他
二、Leetcode刷题练习
1、搜索插入位置
1)、描述
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
(你可以假设数组中无重复元素)
2)、示例
示例 1:
输入: [1,3,5,6], 5
输出: 2
示例 2:
输入: [1,3,5,6], 2
输出: 1
示例 3:
输入: [1,3,5,6], 7
输出: 4
示例 4:
输入: [1,3,5,6], 0
输出: 0
3)、思想
本题可以采用二分法的思想,设置三个指针分别为左边界left,右边界right,中间mid。查找时,先比较mid,若大于mid则查找做右边,小于mid则查找左边。
4)、代码
class Solution:
def searchInsert(self, nums: list[int], target: int) -> int:
#左边界下标为0
left = 0
#有边界下标为数组长度
right = len(nums)
while left < right:
mid = (left + right) // 2
#查找右边
if nums[mid] < target:
left = mid + 1
#查找左边
elif nums[mid] > target:
right = mid
#与mid相等
elif nums[mid] == target:
right = mid
return left
5)、来源
Leetcode(力扣):https://leetcode-cn.com/problems/search-insert-position/
2、快乐数
1)、描述
编写一个算法来判断一个数 n 是不是快乐数。
「快乐数」定义为:对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和,然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。如果 可以变为 1,那么这个数就是快乐数。
如果 n 是快乐数就返回 True ;不是,则返回 False
2)、示例
示例:
输入:19
输出:true
解释:
1^2 + 9^2 = 82
8^2 + 2^2 = 68
6^2 + 8^2 = 100
1^2 + 0^2 + 0^2 = 1
3)、思想
算法分为两部分:
给一个数字 nn,它的下一个数字是什么?
按照一系列的数字来判断我们是否进入了一个循环。
1.第 1 部分我们按照题目的要求做数位分离,求平方和。
2.第 2 部分可以使用 HashSet 完成。每次生成链中的下一个数字时,我们都会检查它是否已经在 HashSet 中。
如果它不在 HashSet 中,我们应该添加它。
如果它在 HashSet 中,这意味着我们处于一个循环中,因此应该返回 false。
我们使用 HashSet 而不是向量、列表或数组的原因是因为我们反复检查其中是否存在某数字。检查数字是否在哈希集中需要 O(1)O(1) 的时间,而对于其他数据结构,则需要 O(n)O(n) 的时间。选择正确的数据结构是解决这些问题的关键部分。
4)、代码
class Solution:
def isHappy(self, n: int) -> bool:
def get_next(n):
total_sum = 0
while n > 0:
n, digit = divmod(n, 10)
total_sum += digit ** 2
return total_sum
seen = set()
while n != 1 and n not in seen:
seen.add(n)
n = get_next(n)
return n == 1
5)、来源
Leetcode(力扣):https://leetcode-cn.com/problems/happy-number/
3、同构字符串
1)、描述
给定两个字符串 s 和 t,判断它们是否是同构的。
如果 s 中的字符可以被替换得到 t ,那么这两个字符串是同构的。
所有出现的字符都必须用另一个字符替换,同时保留字符的顺序。两个字符不能映射到同一个字符上,但字符可以映射自己本身。
2)、示例
示例 1:
输入: s = "egg", t = "add"
输出: true
示例 2:
输入: s = "foo", t = "bar"
输出: false
示例 3:
输入: s = "paper", t = "title"
输出: true
说明:
你可以假设 s 和 t 具有相同的长度。
3)、思想
分别遍历s和t,用字典和列表保存每个字符的编号。(编号从1开始,同字符的编号相同)返回s和t的编号列表是否相同。
4)、代码
class Solution:
def isIsomorphic(self, s: str, t: str) -> bool:
dct = {}
for i in range(len(s)):
if s[i] not in dct:
if t[i] in dct.values():
return False
dct[s[i]] = t[i]
else:
if dct[s[i]] != t[i]:
return False
return True
5)、来源
Leetcode(力扣):https://leetcode-cn.com/problems/isomorphic-strings/
4、有效的字母异位词
1)、描述
给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。
2)、示例
示例 1:
输入: s = "anagram", t = "nagaram"
输出: true
示例 2:
输入: s = "rat", t = "car"
输出: false
说明:
你可以假设字符串只包含小写字母。
3)、思想
把其中一项用集合set(无重复性质)遍历每个字母的个数,如果每个都相等则为True,只要出现一次不等直接False结束。
4)、代码
class Solution:
def isAnagram(self, s: str, t: str) -> bool:
if len(s) != len(t):
return False
set1 = set(s)
for i in set1:
if s.count(i) != t.count(i):
return False
return True
5)、来源
Leetcode(力扣):https://leetcode-cn.com/problems/valid-anagram/
5、单词规律
1)、描述
给定一种规律 pattern 和一个字符串 str ,判断 str 是否遵循相同的规律。
这里的 遵循 指完全匹配,例如, pattern 里的每个字母和字符串 str 中的每个非空单词之间存在着双向连接的对应规律。
2)、示例
示例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 包含了由单个空格分隔的小写字母。
3)、思想
本体可以按照205题同构字符串的方法去想,不再赘述
4)、代码
class Solution:
def wordPattern(self, pattern: str, str: str) -> bool:
t = str.split()
if len(pattern) != len(t):
return False
dct = {}
for i in range(len(pattern)):
if pattern[i] not in dct:
if t[i] in dct.values():
return False
dct[pattern[i]] = t[i]
else:
if dct[pattern[i]] != t[i]:
return False
return True
5)、来源
Leetcode(力扣):https://leetcode-cn.com/problems/word-pattern/
6、 两个数组的交集
1)、描述
给定两个数组,编写一个函数来计算它们的交集。
2)、示例
示例 1:
输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2]
示例 2:
输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出:[9,4]
说明:
输出结果中的每个元素一定是唯一的。
我们可以不考虑输出结果的顺序。
3)、思想
遍历其中一个数组,判断此数组中的元素:
1.是否在另一个数组里;
2.是否已经在交集中。
同时满足条件后,添加到交集中。
4)、代码
class Solution:
def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
array = []
for m in nums1:
if m in nums2 and m not in array:
array.append(m)
return array
5)、来源
Leetcode(力扣):https://leetcode-cn.com/problems/intersection-of-two-arrays/
7、两个数组的交集 II
1)、描述
给定两个数组,编写一个函数来计算它们的交集
2)、示例
示例 1:
输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2,2]
示例 2:
输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出:[4,9]
说明:
输出结果中每个元素出现的次数,应与元素在两个数组中出现次数的最小值一致。
我们可以不考虑输出结果的顺序。
3)、思想
如果两个数组是有序的,则可以便捷地计算两个数组的交集。首先对两个数组进行排序,然后使用两个指针遍历两个数组。
初始时,两个指针分别指向两个数组的头部。每次比较两个指针指向的两个数组中的数字,如果两个数字不相等,则将指向较小数字的指针右移一位,如果两个数字相等,将该数字添加到答案,并将两个指针都右移一位。当至少有一个指针超出数组范围时,遍历结束。
4)、代码
class Solution:
def intersect(self, nums1: List[int], nums2: List[int]) -> List[int]:
nums1.sort()
nums2.sort()
length1, length2 = len(nums1), len(nums2)
intersection = list()
index1 = index2 = 0
while index1 < length1 and index2 < length2:
if nums1[index1] < nums2[index2]:
index1 += 1
elif nums1[index1] > nums2[index2]:
index2 += 1
else:
intersection.append(nums1[index1])
index1 += 1
index2 += 1
return intersection
5)、来源
Leetcode(力扣):https://leetcode-cn.com/problems/intersection-of-two-arrays-ii/
8、分割数组的最大值
1)、描述
给定一个非负整数数组和一个整数 m,你需要将这个数组分成 m 个非空的连续子数组。设计一个算法使得这 m 个子数组各自和的最大值最小。
注意:
数组长度 n 满足以下条件:
1 ≤ n ≤ 1000
1 ≤ m ≤ min(50, n)
2)、示例
输入:
nums = [7,2,5,10,8]
m = 2
输出:
18
解释:
一共有四种方法将nums分割为2个子数组。
其中最好的方式是将其分为[7,2,5] 和 [10,8],
因为此时这两个子数组各自的和的最大值为18,在所有情况中最小。
3)、思想
本题可以使用贪心算法,贪心地模拟分割的过程,从前到后遍历数组,用 sum 表示当前分割子数组的和,cnt 表示已经分割出的子数组的数量(包括当前子数组),那么每当 sum 加上当前值超过了 x,我们就把当前取的值作为新的一段分割子数组的开头,并将 cnt 加 1。遍历结束后验证是否 cnt 不超过 m。
4)、代码
class Solution:
def splitArray(self, nums: List[int], m: int) -> int:
def check(x: int) -> bool:
total, cnt = 0, 1
for num in nums:
if total + num > x:
cnt += 1
total = num
else:
total += num
return cnt <= m
left = max(nums)
right = sum(nums)
while left < right:
mid = (left + right) // 2
if check(mid):
right = mid
else:
left = mid + 1
return left
5)、来源
Leetcode(力扣):https://leetcode-cn.com/problems/split-array-largest-sum/
9、根据字符出现频率排序
1)、描述
给定一个字符串,请将字符串里的字符按照出现的频率降序排列。
2)、示例
示例 1:
输入:
"tree"
输出:
"eert"
解释:
'e'出现两次,'r'和't'都只出现一次。
因此'e'必须出现在'r'和't'之前。此外,"eetr"也是一个有效的答案。
示例 2:
输入:
"cccaaa"
输出:
"cccaaa"
解释:
'c'和'a'都出现三次。此外,"aaaccc"也是有效的答案。
注意"cacaca"是不正确的,因为相同的字母必须放在一起。
示例 3:
输入:
"Aabb"
输出:
"bbAa"
解释:
此外,"bbaA"也是一个有效的答案,但"Aabb"是不正确的。
注意'A'和'a'被认为是两种不同的字符。
3)、思想
本题可以使用python内置的counter解决
简单介绍一下 Counter。
1.它是一个用来统计出现次数的类,在 collections 包里。
2.Counter(s) 就可以返回一个类似字典的结构,键是s中的项,值是这个项出现的次数。
3.对于字符串,就是每个字符和它出现的次数。
4.Counter类有一个函数,叫most_common(int n),可以返回最常出现的几项,如果不加参数,就全部返回,相当于按照出现次数从大到小输出。
输出的格式形如 [('s',4),('a',3)]。
即字符s出现4次,a出现3次。
4)、代码
from collections import Counter
class Solution:
def frequencySort(self, s: str) -> str:
return ''.join(i * j for i,j in Counter(s).most_common())
5)、来源
Leetcode(力扣):https://leetcode-cn.com/problems/sort-characters-by-frequency/
10、有序数组中的单一元素
1)、描述
给定一个只包含整数的有序数组,每个元素都会出现两次,唯有一个数只会出现一次,找出这个数。
2)、示例
示例 1:
输入: [1,1,2,3,3,4,4,8,8]
输出: 2
示例 2:
输入: [3,3,7,7,10,11,11]
输出: 10
注意: 您的方案应该在 O(log n)时间复杂度和 O(1)空间复杂度中运行。
3)、思想
只对偶数进行搜索:
1.奇数长度的数组首尾元素索引都为偶数,因此我们可以将 lo 和 hi 设置为数组首尾。
2.我们需要确保 mid 是偶数,如果为奇数,则将其减 1。
3.然后,我们检查 mid 的元素是否与其后面的索引相同。
4.如果相同,则我们知道 mid 不是单个元素。且单个元素在 mid 之后。则我们将 lo 设置为 mid + 2。
5.如果不是,则我们知道单个元素位于 mid,或者在 mid 之前。我们将 hi 设置为 mid。
6.一旦 lo == hi,则当前搜索空间为 1 个元素,那么该元素为单个元素,我们将返回它。
4)、代码
class Solution:
def singleNonDuplicate(self, nums: List[int]) -> int:
lo = 0
hi = len(nums) - 1
while lo < hi:
mid = lo + (hi - lo) // 2
if mid % 2 == 1:
mid -= 1
if nums[mid] == nums[mid + 1]:
lo = mid + 2
else:
hi = mid
return nums[lo]
5)、来源
Leetcode(力扣):https://leetcode-cn.com/problems/single-element-in-a-sorted-array/