目标
这次的作业主要是完成
- 多个数据的查找(nSum):排序和对撞指针
- 多个点共线、距离相等:查表
- 滑动数组
多个数据的查找
问题是在给定数组nums内找出和为target的n个数组合(n=2,3,4…),甚至有结果最接近target的n个数组合。
- 思路1是2Sum中的查找表法:遍历数组过程中,当遍历到元素v时,可以只看v前面的元素时候含有target-v的元素存在,如果没有就将v放入查找表中,继续查找下一个解,否则查找成功返回解。
- 思路2是将数组排序后,采用两指针对撞的方法找到l和r使得nums[l]+nums[r]=target-nums[i]-nums[j],注意重复结果的处理。
LeetCode 1 两数之和
问题: 给出一个整型数组nums,返回这个数组中两个数字的索引值i和j,使得nums[i] + nums[j]等于一个给定的target值,两个索引不能相等。如:nums= [2,7,11,15],target=9 返回[0,1]
思路: 一种是采用查找表避免嵌套循环,另一种是双指针碰撞。
代码
class Solution:
'''法1:采用查表找相同'''
def twoSum(self, nums: List[int], target: int) -> List[int]:
record = dict()
for i in range(len(nums)):
complement = target - nums[i]
# 已经在之前的字典中找到这个值
if record.get(complement) is not None:
res = [i,record[complement]]
return res
record[nums[i]] = i
'''法2:采用双指针碰撞'''
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
_nums = list(enumerate(nums))
_nums.sort(key=lambda x:x[1])
lo,hi = 0, len(nums)-1
while lo < hi:
if _nums[lo][1] + _nums[hi][1] > target:
hi -= 1
elif _nums[lo][1] + _nums[hi][1] < target:
lo += 1
else:
return _nums[lo][0],_nums[hi][0]
LeetCode 15 三数之和
问题: 给出一个整型数组,寻找其中的所有不同的三元组(a,b,c),使得a+b+c=0注意:答案中不可以包含重复的三元组。如:nums = [-1, 0, 1, 2, -1, -4],结果为:[[-1, 0, 1],[-1, -1, 2]]
思路: 固定a,然后找到b和c,然后循环遍历a。注意去重。
代码:
class Solution:
def threeSum(self, nums: [int]) -> [[int]]:
nums.sort()
res = []
for i in range(len(nums)-2):
# 因为是排序好的数组,如果最小的都大于0可以直接排除
if nums[i] > 0: break
# 排除i的重复值
if i > 0 and nums[i] == nums[i-1]: continue
l,r = i+1, len(nums)-1
while l < r:
sum = nums[i] + nums[l] + nums[r]
if sum == 0:
res.append([nums[i],nums[l],nums[r]])
l += 1
r -= 1
while l < r and nums[l] == nums[l-1]: l += 1
while l < r and nums[r] == nums[r+1]: r -= 1
elif sum < 0:
l += 1
else:
r -= 1
return res
LeetCode 16 最接近的三数之和t
问题: 给出一个整形数组,寻找其中的三个元素a,b,c,使得a+b+c的值最接近另外一个给定的数字target。
如:给定数组 nums = [-1,2,1,-4], 和 target = 1.与 target 最接近的三个数的和为 2. (-1 + 2 + 1 = 2).
思路: 不是找相等的target,而是找最近的。因此开始随机设定三个数的和为结果值,然后比较判断三数只和是否与target相等,如果相等直接返回,如果不等则判断三数之和与target之间的差是否小于这个结果,如果小于则替换并保存和的结果值。
class Solution:
def threeSumClosest(self, nums: List[int], target: int) -> int:
nums.sort()
diff = abs(nums[0]+nums[1]+nums[2]-target)
res = nums[0] + nums[1] + nums[2]
for i in range(len(nums)):
l,r = i+1,len(nums)-1
t = target - nums[i]
while l < r:
if nums[l] + nums[r] == t:
return nums[i] + t
else:
if abs(nums[l]+nums[r]-t) < diff:
diff = abs(nums[l]+nums[r]-t)
res = nums[i]+nums[l]+nums[r]
if nums[l]+nums[r] < t:
l += 1
else:
r -= 1
return res
LeetCode 18 四数之和
问题: 给出一个整形数组,寻找其中的所有不同的四元组(a,b,c,d),使得a+b+c+d等于一个给定的数字target。
如:nums = [1, 0, -1, 0, -2, 2],target = 0结果为:[[-1, 0, 0, 1],[-2, -1, 1, 2],[-2, 0, 0, 2]]
思路: 类似3Sum的思路,注意重复值的处理。
代码
class Solution:
def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
nums.sort()
res = []
if len(nums) < 4: return res
if len(nums) == 4 and sum(nums) == target:
res.append(nums)
return res
for i in range(len(nums)-3):
if i > 0 and nums[i] == nums[i-1]: continue
for j in range(i+1,len(nums)-2):
if j > i+1 and nums[j] == nums[j-1]: continue
l,r = j+1, len(nums)-1
while l < r:
sum_value = nums[i] + nums[j] + nums[l] + nums[r]
if sum_value == target:
res.append([nums[i],nums[j],nums[l],nums[r]])
l += 1
r -= 1
while l < r and nums[l] == nums[l-1]: l += 1
while l < r and nums[r] == nums[r+1]: r -= 1
elif sum_value < target:
l += 1
else:
r -= 1
return res
LeetCode 454 四数相加 II
问题: 给出四个整形数组A,B,C,D,寻找有多少i,j,k,l的组合,使得A[i]+B[j]+C[k]+D[l]=0。其中,A,B,C,D中均含有相同的元素个数N,且0<=N<=500;
输入: A = [ 1, 2] B = [-2,-1] C = [-1, 2] D = [ 0, 2] 输出:2
没时间看了。。
LeetCode 49 字母异位词分组
问题: 给出一个字符串数组,将其中所有可以通过颠倒字符顺序产生相同结果的单词进行分组。
示例:输入: [“eat”, “tea”, “tan”, “ate”, “nat”, “bat”],
输出:[[“ate”,“eat”,“tea”],[“nat”,“tan”],[“bat”]]
说明:所有输入均为小写字母。不考虑答案输出的顺序。
思路: 本来我是以set记录每个单词的字库,结果遇到了’bob’和’bo’,所以单词长度或者词频也很重要,因此转换成dict保存单词信息。结果一看答案给每个单词排序,然后以计数-单词为key-value建立dict,然后返回dict的结果,所以排序666.
代码:
class Solution:
def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
from collections import defaultdict
_dict = {}
for str in strs:
_str = ''.join(sorted(list(str)))
if _str not in _dict:
_dict[_str] = [str]
else:
_dict[_str].append(str)
return [v for k,v in _dict.items()]
LeetCode 447. 回旋镖的数量
问题: 给出一个平面上的n个点,寻找存在多少个由这些点构成的三元组(i,j,k),使得i,j两点的距离等于i,k两点的距离。其中n最多为500,且所有的点坐标的范围在[-10000,10000]之间。
输入:[[0,0],[1,0],[2,0]] 输出:2
解释:两个结果为: [[1,0],[0,0],[2,0]] 和 [[1,0],[2,0],[0,0]]
思路: 固定i点,遍历数组后找到所有与i相等距离的点,这些点排列即可得到包含i点的结果。然后针对不同的i点将所有的情况计数即为所求。
代码:
class Solution:
def numberOfBoomerangs(self, points: List[List[int]]) -> int:
def func(x1,y1):
from collections import Counter
_dict = Counter([(x2-x1)**2 + (y2-y1)**2 for x2,y2 in points])
return sum(t*(t-1) for k,t in _dict.items())
return sum(func(x1,y1) for x1,y1 in points)
class Solution:
def numberOfBoomerangs(self, points: List[List[int]]) -> int:
res = 0
from collections import Counter
for i in points:
record = Counter()
for j in points:
if i != j:
record[self.dis(i,j)] += 1
for k,v in record.items():
res += v*(v-1)
return res
def dis(self,point1,point2):
return (point1[0]-point2[0]) ** 2 + (point1[1]-point2[1]) ** 2
LeetCode 149. 直线上最多的点数
问题: 给定一个二维平面,平面上有 n 个点,求最多有多少个点在同一条直线上。
示例 1:输入: [[1,1],[2,2],[3,3]] 输出: 3
示例 2:输入: [[1,1],[3,2],[5,3],[4,1],[2,3],[1,4]] 输出: 4
思路: 与上一题距离相同相比,这道题要求共线,即固定i点后,余下的点与i连线的斜率相等或者点重合。这道题我最不喜欢,因为截至写文时候,还没有解答出来,总是不能解决[[0,0],[0,0]]问题或者[[1,1],[2,2],[1,1],[2,2]]。
斜率的计算(y2-y1)/(x2-x1)
滑动数组
滑动数组部分解决的问题是对数组中待查找的2个数值索引有限制,采用滑动数组方法可以减少重复遍历,减少计算量。思路是在set()集合结构为窗,当集合未填满则不处理,填满情况下继续添加则去除集合最前面的值,在最后一位补充新增值。
LeetCode 219. 存在重复元素 II
问题 给出一个整形数组nums和一个整数k,是否存在索引i和j,使得nums[i]==nums[j],且i和J之间的差不超过k。
示例1:输入: nums = [1,2,3,1], k = 3输出: true
示例 2:输入: nums = [1,2,3,1,2,3], k = 2输出: false
思路: 由于滑动数组的元素都不同且不考虑位置,因此选择set作为数据结构。
代码:
class Solution:
def containsNearbyDuplicate(self, nums: List[int], k: int) -> bool:
rec = set()
for i,n in enumerate(nums):
if n in rec: return True
rec.add(n)
if len(rec) == k+1:
rec.remove(nums[i-k])
return False
LeetCode 220 Contains Dupliccate Ⅲ
问题: 给定一个整数数组,判断数组中是否有两个不同的索引 i 和 j,使得nums [i] 和nums [j]的差的绝对值最大为 t,并且 i 和 j 之间的差的绝对值最大为 ķ。
示例 1:输入: nums = [1,2,3,1], k = 3, t = 0 输出: true
示例 2:输入: nums = [1,0,1,1], k = 1, t = 2 输出: true
示例 3:输入: nums = [1,5,9,1,5,9], k = 2, t = 3 输出: false
思路: 与上一题相比,判断条件由查找表中有nums[i]变为了查找比v-t大的最小元素。
代码:
class Solution:
def containsNearbyAlmostDuplicate(self, nums: List[int], k: int, t: int) -> bool:
record = set()
for i in range(len(nums)):
if len(record) != 0:
rec = list(record)
find_index = self.lower_bound(rec,nums[i]-t)
if find_index != -1 and rec[find_index] <= nums[i] + t:
return True
record.add(nums[i])
if len(record) == k + 1:
record.remove(nums[i - k])
return False
def lower_bound(self, nums, target):
low, high = 0, len(nums)-1
while low<high:
mid = int((low+high)/2)
if nums[mid] < target:
low = mid+1
else:
high = mid
return low if nums[low] >= target else -1
参考:
https://github.com/datawhalechina/team-learning-program/blob/master/LeetCodeClassification/3.%E6%9F%A5%E6%89%BE.md#leetcode-1-two-sum