前文
继上一篇:Leetcode题121/122/167/169/189/217/219/268/283,Python多种解法(二),这次开始分享数组medium的题,说实在的,到了中等题,很难自己过了,只有少部分和easy一样难度的题才能过,打击还是有的,不过还是继续刷吧,等二刷三刷的时候,相信自己的代码能力和算法能力应该会有提高的!那开始上题了!
11. Container With Most Water
class Solution1:
"""
从首尾开始计算初步的最大值,然后从首尾短的那边开始往中间推移,不断地计算最大,直到中间汇合则break出来,有点类似于贪心算法,即一直取当前最大的面积.
Runtime: 60 ms, faster than 79.17% of Python3 online submissions for Container With Most Water.
"""
def maxArea(self, height):
"""
:type height: List[int]
:rtype: int
"""
start,end,now_max = 0,len(height)-1,0
while start < end:
now_max = max(now_max, (end-start)*min(height[start], height[end]))
if height[end] < height[start]:
end -= 1
else:
start += 1
return now_max
class Solution2:
"""
讨论区的解法,时间复杂度要优于上方,即从0开始,分为i\j从两端开始往里收缩,原理和上方差不多,但速度就是要快一些.
Runtime: 48 ms, faster than 100.00% of Python3 online submissions for Container With Most Water.
"""
def maxArea(self, height):
"""
:type height: List[int]
:rtype: int
"""
area, i, j = 0, 0, len(height) - 1
while i < j:
if height[i] <= height[j]:
less = height[i]
i += 1
else:
less = height[j]
j -= 1
new = (j - i + 1) * less
if new > area:
area = new
return area
15. 3Sum
class Solution:
"""
相当于利用快排的原理,先排序然后从头选定一个数,接着从剩下的数里依次从首尾选择两个数,如果满足条件和为0,则缩小范围继续;没满足也缩小范围继续。
Runtime: 700 ms, faster than 93.20% of Python3 online submissions for 3Sum.
Memory Usage: 15.9 MB, less than 100.00% of Python3 online submissions for 3Sum.
"""
def threeSum(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
res = []
nums.sort()
for i in range(len(nums)):
if i>0 and nums[i] == nums[i-1]:
continue
target = -nums[i]
l, r = i+1, len(nums)-1
while l < r:
ans = nums[l] + nums[r]
if ans == target:
res.append([nums[i], nums[l], nums[r]])
while l<r and nums[l] == nums[l+1]:
l = l + 1
l += 1
while l<r and nums[r] == nums[r-1]:
r -= 1
r -= 1
elif ans > target:
r -= 1
else:
l += 1
return res
16. 3Sum Closest
class Solution(object):
"""
和15题的思路差不多,利用先求得三者的和,然后进行与target最小值的判断,保持当前最小值sum1,然后逐步推进数组,直到l>=r,所以是个O(n^2)
Runtime: 72 ms, faster than 75.37% of Python online submissions for 3Sum Closest.
Memory Usage: 11 MB, less than 100.00% of Python online submissions for 3Sum Closest.
"""
def threeSumClosest(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: int
"""
nums.sort()
n = len(nums)
sum1 = nums[0] + nums[1] + nums[2]
for i in range(n):
l,r = i+1, n-1
while l < r:
res = nums[i] + nums[l] + nums[r]
if abs(sum1-target) > abs(res-target):
sum1 = res
if res < target:
while l < r and nums[l]==nums[l-1]:
l += 1
l += 1
else:
if res == target:
return res
while l < r and nums[r] == nums[r-1]:
r -= 1
r -= 1
return sum1
18. 4Sum
import collections
import itertools
class Solution(object):
"""
直接沿用3Sum的解法,多添加一层罢了,所以效率也是低的不行,不过这种方法算是最容易想出的。
Runtime: 808 ms, faster than 22.63% of Python online submissions for 4Sum.
Memory Usage: 10.7 MB, less than 100.00% of Python online submissions for 4Sum.
"""
def fourSum(self, nums, target):
nums.sort()
ans = []
for a in range(len(nums)-3):
if a > 0 and nums[a] == nums[a-1]:
continue
for b in range(a+1,len(nums)-2):
if b > a+1 and nums[b] == nums[b-1]:
continue
c, d = b+1, len(nums)-1
while c < d:
tot = nums[a]+nums[b]+nums[c]+nums[d]
if tot == target:
ans.append([nums[a],nums[b],nums[c],nums[d]])
if tot <= target:
c += 1
while nums[c] == nums[c-1] and c < d:
c += 1
if tot >= target:
d -= 1
while nums[d] == nums[d+1] and c < d:
d -= 1
return ans
class Solution2(object):
"""
这里采用python的高级解法,这些用法看的我眼花缭乱,感觉自己学了一个假python;
通过defaultdict建立字典,可以在key不存在时不报错,而是默认value为list即空列表;
用combinations找出所有的index、value能构成的二元元祖;
接着遍历two_sum的key,也就是nums任意两个数的和,因为value是set,所以接下来都是围绕set的操作;
set.isdisjoint是求交集,即两个set没有相同的部分返回真,所以完美规避了两个pair相同;
pair1|pair2则是求并集,即两个set的所有元素,;
这里的sorted也是必备的,需要把不同的索引但是重复的数字给删除了;
关于del two_sum[t],也可以不删除,因为去掉这句代码照常运行,不过速度会慢点,这点还不清楚原因。
Runtime: 168 ms, faster than 70.47% of Python online submissions for 4Sum.
Memory Usage: 23.5 MB, less than 100.00% of Python online submissions for 4Sum.
"""
def fourSum(self, nums, target):
two_sum = collections.defaultdict(list)
res = set()
for (n1, i1), (n2, i2) in itertools.combinations(enumerate(nums), 2):
two_sum[i1+i2].append({n1, n2})
for t in list(two_sum.keys()):
if not two_sum[target-t]:
continue
for pair1 in two_sum[t]:
for pair2 in two_sum[target-t]:
if pair1.isdisjoint(pair2):
res.add(tuple(sorted(nums[i] for i in pair1 | pair2)))
del two_sum[t]
return [list(r) for r in res]
31. Next Permutation
class Solution(object):
"""
这道题光是题目就难倒了我,简单查了下,原来是找列表的所有排列情况,并且第一行默认是asc排序,最后一行是desc,依次排列出所有情况,
然后给出任一行,推测出它的下一行,比如[1,2,3]就有以下情况:
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
我个人是想了非常多的if..else..,结果提交时还是各种各样的错,最后看了网上答案,原来就是将数学规律写成代码;
数学规律:6 5 4 8 7 3 1
以上面为例,从后往前看,依次递增,直到4的时候结束,那么此时找出从4之后的所有数里比4大的最小数,交换他们,然后升序排序索引2(也就是原来4)
仔细看代码,就是完完整整依据上面的逻辑,不过为了方便,是先将4之后的排序,再交换位置,道理是一样的。
至于如何推导的,我是推导了下,不过比较麻烦,就不多说了,大家可以借助我函数里的三种情况进行推导,就是从后往前,判断每种情况下的变化
Runtime: 28 ms, faster than 100.00% of Python online submissions for Next Permutation.
Memory Usage: 10.8 MB, less than 100.00% of Python online submissions for Next Permutation.
"""
def nextPermutation(self, nums):
"""
:type nums: List[int]
:rtype: void Do not return anything, modify nums in-place instead.
6 5 4 1 3 7 8
6 5 4 1 7 8 3
6 5 4 8 7 3 1
"""
n = len(nums)
i = n - 1
while i > 0 and nums[i] <= nums[i - 1]:
i -= 1
self.reverse(nums, i, n - 1)
if i > 0:
for j in range(i, n):
if nums[j] > nums[i-1]:
self.swap(nums, i-1, j)
break
def reverse(self, nums, i, j):
"""
contains i and j.
"""
for k in range(i, (i + j) / 2 + 1):
self.swap(nums, k, i + j - k)
def swap(self, nums, i, j):
"""
contains i and j.
"""
nums[i], nums[j] = nums[j], nums[i]
33. Search in Rotated Sorted Array
class Solution(object):
"""
不知道有没有和我一样的,想用最原始的方法来做,即先找出被颠倒的那个数,然后再用二分法分左右两次来求解
虽然超时了,不过这个方法真是第一时间在脑海里浮现,由此看来,还得多练
Runtime:time limit exceed
"""
def search(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: int
"""
start = 0
end = len(nums)
while start < end:
mid = (start+end)/2
if nums[mid] < nums[mid-1]:
res = mid
break
elif nums[mid] > nums[start]:
end = mid
elif nums[mid] < nums[start]:
start = mid
if target > nums[0]:
start = 0
end = res
while start < end:
mid = (start+end)/2
if nums[mid] == target:
return mid
elif nums[mid] < target:
start = mid
else:
end = mid
return -1
else:
start = res
end = len(nums)
while start < end:
mid = (start+end)/2
if nums[mid] == target:
return mid
elif nums[mid] < target:
start = mid
else:
end = mid
return -1
class Solution2(object):
"""
讨论区的解法有两种,一种是利用二进制位运算求解,这次暂不研究,这边选取二分法,也是最容易浮现的解法
在判断target>nums[0]时,一开始我的想法是只要判断target>nums[mid]就行了,结果出错,因为有两种情况,
举例来说,target为4,一种是:4,5,0,1,2,此时因为中位数也就是0小于nums[0],所以应该是end = mid-1
另一种target为7,排列是:4,5,6,7,1,2,此时中位数也就是6大于nums[0],所以应该是start=mid+1
Runtime: 20 ms, faster than 100.00% of Python online submissions for Search in Rotated Sorted Array.
Memory Usage: 10.8 MB, less than 93.14% of Python online submissions for Search in Rotated Sorted Array.
"""
def search(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: int
"""
start = 0
end = len(nums)-1
while start <= end:
mid = (start+end)/2
if target == nums[mid]:
return mid
if target >= nums[0]:
if target > nums[mid] and nums[mid] >= nums[0]:
start = mid + 1
else:
end = mid - 1
else:
if target > nums[mid] or nums[mid] >= nums[0]:
start = mid + 1
else:
end = mid - 1
return -1
34. Find First and Last Position of Element in Sorted Array
class MySolution(object):
"""
正常思路,利用二分法来完成,缺点是在找到中间数后要一次从左、从右开始遍历,这也造成了时间复杂度不是很理想
Runtime: 24 ms, faster than 82.77% of Python online submissions for Find First and Last Position of Element in Sorted Array.
Memory Usage: 11.4 MB, less than 60.93% of Python online submissions for Find First and Last Position of Element in Sorted Array.
"""
def searchRange(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[int]
"""
if nums == []:
return [-1, -1]
left_pos = 0
right_pos = len(nums)
while left_pos < right_pos:
middle_pos = (right_pos - left_pos) // 2 + left_pos
middle_val = nums[middle_pos]
if middle_val == target:
left = middle_pos
while left >= 0 and nums[left] == target:
left -= 1
right = middle_pos
while right < len(nums) and nums[right] == target:
right += 1
return [left + 1, right - 1]
elif middle_val < target:
left_pos = middle_pos + 1
else: # middle_val > target:
right_pos = middle_pos
return [-1, -1]
class Solution(object):
"""
利用python语法的index来完成,通过两次index来定位目标位置即可,找不到报错就拦截
Runtime: 20 ms, faster than 100.00% of Python online submissions for Find First and Last Position of Element in Sorted Array.
Memory Usage: 11.9 MB, less than 5.30% of Python online submissions for Find First and Last Position of Element in Sorted Array.
"""
def searchRange(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[int]
"""
try:
return [nums.index(target),len(nums)-list(reversed(nums)).index(target)-1]
except:
return [-1,-1]
39. Combination Sum
class Solution(object):
"""
这道题利用了递归的思想,在函数里再次调用函数来判断target-candidates[i]的数是否在集合里,从而可以知道重复数的排列集合
利用到递归的都相对比较难以理解,下列解法用debug或者在纸上跑一遍,会发现程序走的和预想的有出入,比如把下述代码的print注释去掉,看下结果是否如你所料?
Runtime: 204 ms, faster than 11.86% of Python online submissions for Combination Sum.
Memory Usage: 10.8 MB, less than 33.60% of Python online submissions for Combination Sum.
"""
def combinationSum(self, candidates, target):
"""
:type candidates: List[int]
:type target: int
:rtype: List[List[int]]
"""
# global final_result
final_result = []
candidates.sort()
def findCom(target, temp_list):
if target in candidates:
a = temp_list + [target]
if sorted(a) not in final_result:
final_result.append(a)
for i in [n for n in candidates if n < target]:
# print (i, target,temp_list)
a = temp_list + [i]
findCom(target - i, a)
t = []
findCom(target, t)
return final_result
class Solution1(object):
"""
讨论区流行解法,用到的也是递归,而方法的效率就比上一个高多了,从命名来看是DFS,即Depth-First-Search,深度优先算法
本质我觉得和上一个解法差不多,而且可理解性还没有上个解法好,也是从头开始遍历,然后如果target == 0,也就是找到组合,就往
res里增加组合,没找到,就往path里增加数,直到target<0,则此次递归结束,开始下一个数的递归
Runtime: 92 ms, faster than 47.88% of Python online submissions for Combination Sum.
Memory Usage: 10.6 MB, less than 94.71% of Python online submissions for Combination Sum.
"""
def combinationSum(self, candidates, target):
"""
:type candidates: List[int]
:type target: int
:rtype: List[List[int]]
"""
res = []
candidates.sort()
self.dfs(candidates, target, 0, [], res)
return res
def dfs(self, candidates, target, index, path, res):
if target < 0:
return
if target == 0:
res.append(path)
return
for i in range(index, len(candidates)):
self.dfs(candidates, target-candidates[i], i, path+[candidates[i]], res)
class Solution2(object):
"""
回溯解法,非递归
Runtime: 152 ms, faster than 17.24% of Python online submissions for Combination Sum.
Memory Usage: 10.9 MB, less than 29.63% of Python online submissions for Combination Sum.
"""
def combinationSum(self, candidates, target):
"""
:type candidates: List[int]
:type target: int
:rtype: List[List[int]]
"""
candidates, res, stack, lenth=sorted(set(candidates)), [], [(0, [], target)], len(candidates)
while stack:
i, temp, tar=stack.pop()
while i<lenth and tar>0:
if candidates[i]==tar:res+=temp+[candidates[i]],
stack+=(i, temp+[candidates[i]], tar-candidates[i]),
i+=1
return res
40. Combination Sum II
class Solution(object):
"""
直接修改39的代码,在for下面增加一个判断,比如示例:[10,1,2,7,6,1,5],排完序后为[1,1,2,5,6,7,10]
通过递归直接开始找[1,1]后面的:[1,1,2,5],[1,1,2,6],[1,1,2,7],[1,1,2,10];实际可以debug跑一遍或者纸上
运行一遍,不过递归的确浪费了不少步骤,但这种题递归是最容易想到的解法
Runtime: 84 ms, faster than 43.76% of Python online submissions for Combination Sum II.
Memory Usage: 10.8 MB, less than 35.21% of Python online submissions for Combination Sum II.
"""
def combinationSum2(self, candidates, target):
"""
:type candidates: List[int]
:type target: int
:rtype: List[List[int]]
"""
res = []
candidates.sort()
self.dfs(candidates, target, 0, [], res)
return res
def dfs(self, candidates, target, index, path, res):
if target < 0:
return # backtracking
if target == 0:
res.append(path)
return # backtracking
for i in range(index, len(candidates)):
if i > index and candidates[i] == candidates[i-1]:
continue
self.dfs(candidates, target-candidates[i], i+1, path+[candidates[i]], res)
class Solution2(object):
"""
提供另一种非递归思路,讨论区DP solution,效率也是高的一匹
Runtime: 36 ms, faster than 89.58% of Python online submissions for Combination Sum II.
Memory Usage: 11 MB, less than 6.10% of Python online submissions for Combination Sum II.
"""
def combinationSum2(self, candidates, target):
candidates.sort()
table = [None] + [set() for i in range(target)]
for i in candidates:
if i > target:
break
for j in range(target - i, 0, -1):
table[i + j] |= {elt + (i,) for elt in table[j]}
table[i].add((i,))
return map(list, table[target])
总结
本次记录到此为止,一共九题,多谢观看~