- 搜索插入位置
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。你可以假设数组中无重复元素。
二分查找法:
class Solution:
def search(self,x,nums:list):
low,high=0,len(nums)-1
while low<=high:
mid = (low+high)//2
if x > nums[mid]:
low=mid+1
elif x < nums[mid]:
high=mid-1
else:
return mid
return low
a=Solution()
print(a.search(8,[2,3,5,6]))
- 快乐数
编写一个算法来判断一个数 n 是不是快乐数。
「快乐数」定义为:对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和,然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。如果 可以变为 1,那么这个数就是快乐数。
如果 n 是快乐数就返回 True ;不是,则返回 False 。
集合判定法:
class Solution(object):
def isHappy(self, n):
b=set() #集合用于记录每次的n,看n是否重复出现
while n!=1:
if n in b: #如果n出现多次则会无限循环,不是快乐数
return 'false'
b.add(n)
s=0
while n//10!=0:
s += (n%10)**2
n = n//10
s+=n**2
n=s
else:
return 'true'
a=Solution()
print(a.isHappy(39))
#19时为true
注,官方解释的寻找每位平方之和更清晰明了,将这部分作为一个函数,两部分分离:
sum=0
while x>0:
cur=x%10
sum+=cur*cur
x=x//10
return sum
快慢指针法:
通过反复调用 getNext(n) 得到的链是一个隐式的链表。隐式意味着我们没有实际的链表节点和指针,但数据仍然形成链表结构。起始数字是链表的头 “节点”,链中的所有其他数字都是节点。next 指针是通过调用 getNext(n) 函数获得。
意识到我们实际有个链表,那么这个问题就可以转换为检测一个链表是否有环。因此我们在这里可以使用弗洛伊德循环查找算法。这个算法是两个奔跑选手,一个跑的快,一个跑得慢。在龟兔赛跑的寓言中,跑的快的称为 “乌龟”,跑得快的称为 “兔子”。
不管乌龟和兔子在循环中从哪里开始,它们最终都会相遇。这是因为兔子每走一步就向乌龟靠近一个节点(在它们的移动方向上)。
我们不是只跟踪链表中的一个值,而是跟踪两个值,称为快跑者和慢跑者。在算法的每一步中,慢速在链表中前进 1 个节点,快跑者前进 2 个节点(对 getNext(n) 函数的嵌套调用)。
如果 n 是一个快乐数,即没有循环,那么快跑者最终会比慢跑者先到达数字 1。
如果 n 不是一个快乐的数字,那么最终快跑者和慢跑者将在同一个数字上相遇。
class Solution:
def isHappy(self, n: int) -> bool:
def get_next(number):
total_sum = 0
while number > 0:
number, digit = divmod(number, 10)
total_sum += digit ** 2
return total_sum
slow_runner = n
fast_runner = get_next(n)
while fast_runner != 1 and slow_runner != fast_runner:
slow_runner = get_next(slow_runner)
fast_runner = get_next(get_next(fast_runner))
return fast_runner == 1
a=Solution()
print(a.isHappy(19))
- 同构字符串
给定两个字符串 s 和 t,判断它们是否是同构的。
如果 s 中的字符可以被替换得到 t ,那么这两个字符串是同构的。
所有出现的字符都必须用另一个字符替换,同时保留字符的顺序。两个字符不能映射到同一个字符上,但字符可以映射自己本身。
解法一:
最开始我的想法是将两个字符串分别映射成两个列表,以不同数字代表不同的字母,解法如下:
class Solution:
def isIsomorphic(self, s: str, t: str) -> bool:
dica = {
}
lisa = []
seq=0
for i in range(len(s)):
if s[i] in dica:
lisa.append(dica[s[i]])
else:
dica[s[i]]=seq
lisa.append(seq)
seq += 1
dicb = {
}
lisb = []
seq = 0
for i in range(len(t)):
if t[i] in dicb:
lisb.append(dicb[t[i]])
else:
dicb[t[i]] = seq
lisb.append(seq)
seq += 1
if lisa == lisb :
return True
else:
return False
a=Solution()
print(a.isIsomorphic('abegg','ddadd'))
#[0, 1, 2, 3, 3]
#[0, 0, 1, 0, 0]
#False
此方法代码行数较多,可通过拆解成两个方法,两个字符串分别调用一次映射函数,比较结果,但不明显。
通过让一个字符串直接映射另一个字符串可简化成只循环一次:
解法二:
class Solution:
def isIsomorphic(self, s: str, t: str) -> bool:
dic = {
}
lis = []
for i in range(len(s)):
if s[i] not in dic:
if t[i] in dic.values(): #防止s中两个不同的字母映射到t中的同一个字母
return False
dic[s[i]] = t[i]
else:
if dic[s[i]]!=t[i]:
return False
return True
a=Solution()
print(a.isIsomorphic('add','egg'))
还有一种更简单的解法:
解法三:
Python index() 方法检测字符串中是否包含子字符串 str ,如果指定 beg(开始) 和 end(结束) 范围,则检查是否包含在指定范围内,该方法与 python find()方法一样,只不过如果str不在 string中会报一个异常。
返回值:如果包含子字符串返回开始的索引值,否则抛出异常。
根据这个特性,两个字符串若是同构,得到的索引就相等,得解法:
class Solution:
def isIsomorphic(self, s: str, t: str) -> bool:
for i in range(len(s)):
if s.index(s[i]) != t.index(t[i]):
return False
return True
a=Solution()
print(a.isIsomorphic('egg','add'))
- 有效的字母异位词
给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。
你可以假设字符串只包含小写字母。
解法一:
为每个字符串建立一个字典,字典键值分别是字符串中的字符及其出现的次数,两个字典相同则代表两个字符串是字母异位词
class Solution:
def judge(self,x):
dic = {
}
for i in range(len(x)):
if x[i] not in dic :
dic[x[i]]=1
else:
dic[x[i]]+=1
return dic
def isIsomorphic(self, s: str, t: str):
if self.judge(s)==self.judge(t):
return 'true'
else:
return 'false'
a=Solution()
print(a.isIsomorphic('addcas','ascdda'))
方法二:
将字符排序后比较结果:
class Solution:
def isIsomorphic(self, s: str, t: str):
if sorted(s)==sorted(t):
return 'true'
else:
return 'false'
a=Solution()
print(a.isIsomorphic('addcad','ascdda'))
方法三:使用集合,然后逐个字符计算出现次数进行比较
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
a=Solution()
print(a.isAnagram('addcas','ascdda'))
方法四:使用python内置模块collections:
class Solution:
def isAnagram(self, s: str, t: str) -> bool:
return collections.Counter(s) == collections.Counter(t)
a=Solution()
print(a.isAnagram('addcad','ascdda'))
- 单词规律
给定一种规律 pattern 和一个字符串 str ,判断 str 是否遵循相同的规律。
这里的 遵循 指完全匹配,例如, pattern 里的每个字母和字符串 str 中的每个非空单词之间存在着双向连接的对应规律。
示例1:
输入: pattern = “abba”, str = “dog cat cat dog”
输出: true
示例 2:
输入:pattern = “abba”, str = “dog cat cat fish”
输出: false
解法一:
此题类似前边的同构字符串,可以将str用split函数拆分为一个列表,建立pattern中字符与列表中字符串的映射即可:
class Solution:
def wordPattern(self, pattern: str, str: str) -> bool:
dic={
}
s=str.split(' ')
if len(s)!=len(pattern):
return False
for i in range(len(pattern)):
if pattern[i] not in dic:
if s[i] in dic.values():
return False
else:
dic[pattern[i]] = s[i]
else:
if dic[pattern[i]] != s[i]:
return False
return True
a=Solution()
print(a.wordPattern('abba','dog cat cat dog'))
方法二:
也可使用index索引:
class Solution:
def wordPattern(self, pattern: str, str: str) -> bool:
res = str.split()
return list(map(pattern.index, pattern)) == list(map(res.index, res))
a=Solution()
print(a.wordPattern('abba','dog cat cat dog'))
- 两个数组的交集
给定两个数组,编写一个函数来计算它们的交集。
示例 1:
输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2]
示例 2:
输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出:[9,4]
说明:
输出结果中的每个元素一定是唯一的。
我们可以不考虑输出结果的顺序。
方法一:
首先可以使用集合求交集的操作,set1&set2
class Solution(object):
def intersection(self, nums1:list, nums2:list):
a=set(nums1)
b=set(nums2)
return list(a&b)
a=Solution()
print(a.intersection([9,4,9,8],[4,8]))
解法二:
转成集合后遍历:
class Solution(object):
def intersection(self, nums1:list, nums2:list):
a=set(nums1)
b=set(nums2)
c=[]
for i in a:
if i in b:
c.append(i)
return c
a=Solution()
print(a.intersection([9,4,9,8],[4,8]))
- 两个数组的交集 II
给定两个数组,编写一个函数来计算它们的交集。
示例 1:
输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2,2]
示例 2:
输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出:[4,9]
说明:
输出结果中每个元素出现的次数,应与元素在两个数组中出现次数的最小值一致。
我们可以不考虑输出结果的顺序。
进阶:
如果给定的数组已经排好序呢?你将如何优化你的算法?
如果 nums1 的大小比 nums2 小很多,哪种方法更优?
如果 nums2 的元素存储在磁盘上,内存是有限的,并且你不能一次加载所有的元素到内存中,你该怎么办?
解法一:
遍历查找相交的元素,在查找该元素在两个数组中出现的最小次数:
class Solution(object):
def intersection(self, nums1:list, nums2:list):
c = [] #c用于记录结果
ever=[] #ever 用于防止值相同的数重复进行下一步循环
for i in nums1:
if i in nums2 and i not in ever:
n=min(nums1.count(i),nums2.count(i)) #两个数组中都有的值,计算其在两个数组中出现的最小次数
c.extend([i]*n)
ever.append(i)
return c
a=Solution()
print(a.intersection([3,3,8,8,8,8],[3,3,3,3,8,8]))
方法二:
选择较短的一个数组,为其建立一个字典(数字:数字出现的次数),
各个数字出现的次数统计完毕后,遍历另一个数组,若其中的数字在字典中且,出现的次数大于零(如【3,3,8,8,8,8】和【8,8,3,3,3,3】,防止结果变成【3,3,3,3,8,8,8,8】),则将该数字加入结果集中,最后返回结果集即可
class Solution(object):
def intersection(self, nums1:list, nums2:list):
c={
}
result=[]
if len(nums2)<len(nums1):
return self.intersection(nums2,nums1)
for i in nums1:
if i in c:
c[i]+=1
else:
c[i]=1
for i in nums2:
if i in c and c[i]>0:
result.append(i)
c[i]-=1
return result
a=Solution()
print(a.intersection([3,3,8,8,8,8],[3,3,3,3,8,8]))
方法三:排序法
将两个数组排序后方便比较
class Solution(object):
def intersection(self, nums1:list, nums2:list):
nums1.sort()
nums2.sort()
i=j=0
result=[]
while i <len(nums1) and j <len(nums2):
if nums1[i]==nums2[j]:
result.append(nums1[i])
i+=1
j+=1
elif nums1[i]>nums2[j]:
j+=1
else:
i+=1
return result
a=Solution()
print(a.intersection([3,4,8,8,8,8],[3,3,3,3,7,8]))
- 分割数组的最大值
给定一个非负整数数组和一个整数 m,你需要将这个数组分成 m 个非空的连续子数组。设计一个算法使得这 m 个子数组各自和的最大值最小。
注意:
数组长度 n 满足以下条件:
1 ≤ n ≤ 1000
1 ≤ m ≤ min(50, n)
示例:
输入:
nums = [7,2,5,10,8]
m = 2
输出:
18
解释:
一共有四种方法将nums分割为2个子数组。
其中最好的方式是将其分为[7,2,5] 和 [10,8],
因为此时这两个子数组各自的和的最大值为18,在所有情况中最小。
方法一:
class Solution:
def splitArray(self, nums: list, m: int) -> int:
n = len(nums)
f = [[10 ** 18] * (m + 1) for _ in range(n + 1)]
sub = [0]
for elem in nums:
sub.append(sub[-1] + elem)
f[0][0] = 0
for i in range(1, n + 1):
for j in range(1, min(i, m) + 1):
for k in range(i):
f[i][j] = min(f[i][j], max(f[k][j - 1], sub[i] - sub[k]))
return f[n][m]
a=Solution()
print(a.splitArray([7,2,5,10,8],3))
- 根据字符出现频率排序
给定一个字符串,请将字符串里的字符按照出现的频率降序排列。
示例 1:
输入:
“tree”
输出:
“eert”
解释:
'e’出现两次,'r’和’t’都只出现一次。
因此’e’必须出现在’r’和’t’之前。此外,"eetr"也是一个有效的答案。
示例 2:
输入:
“cccaaa”
输出:
“cccaaa”
解释:
'c’和’a’都出现三次。此外,"aaaccc"也是有效的答案。
注意"cacaca"是不正确的,因为相同的字母必须放在一起。
示例 3:
输入:
“Aabb”
输出:
“bbAa”
解释:
此外,"bbaA"也是一个有效的答案,但"Aabb"是不正确的。
注意’A’和’a’被认为是两种不同的字符。
方法一:大根堆
import collections
import heapq
class Solution:
def frequencySort(self, s: str) -> str:
# 大顶堆
countFrequency = collections.defaultdict(int)
for i in s:
countFrequency[i] += 1
lst = []
heapq.heapify(lst)
for i in countFrequency:
for j in range(countFrequency[i]):
heapq.heappush(lst, (-countFrequency[i], i))
return ''.join([heapq.heappop(lst)[1] for _ in range(len(s))])
a=Solution()
print(a.frequencySort('tree'))
- 有序数组中的单一元素
给定一个只包含整数的有序数组,每个元素都会出现两次,唯有一个数只会出现一次,找出这个数。
示例 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)空间复杂度中运行。
方法一:
class Solution:
def singleNonDuplicate(self, nums: list) -> int:
if len(nums) == 1:
return nums[0]
left = 0
right = len(nums)-1
while left <= right:
mid = (left + right) // 2
if mid % 2 == 0 and mid + 1 < len(nums): # mid是偶数(nums[mid]前面有偶数个元素)
if nums[mid] == nums[mid+1]: # mid前面没有单一元素
left = mid + 1
else: # mid前面有单一元素
right = mid - 1
elif mid % 2 != 0 and mid + 1 < len(nums): # mid是奇数(nums[mid]前面有奇数个元素)
if nums[mid] == nums[mid+1]: # mid前面有单一元素
right = mid - 1
else: # mid前面没有单一元素
left = mid + 1
else:
return nums[mid]
return nums[left]
a=Solution()
print(a.singleNonDuplicate( [1,1,2,3,3,4,4,8,8]))