数据结构与算法-数组题
2017-12-12 本文已影响53人
sylvainwang
1. 3-sums -leetcode 15
Given an array S of n integers, are there elements a, b, c in S such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero.
给一个整数数组,找出3个数,和为0
For example, given array S = [-1, 0, 1, 2, -1, -4],
A solution set is:
[
[-1, 0, 1],
[-1, -1, 2]
]
note:做法自然是base于2-sum,遍历数组的每一个数为target,寻找其他数的和为该数的负数 -target
关键在于时间复杂度的优化,复杂度O(n^2)
优化点1: 优先对数组排序,这样碰到某一个target,只用对其后的数组求该-target的2sum,同时能保证最后返回的三元组一定有序
优化点2:结果用set()来存,这样添加结果时候要用.add()方法,将结果三元组保存为tuple (target, -target-i, i) ,
而不能是list [target, -target -i, i] (python里list是unhashable,不能加到set中,最终结果map成list
优化点3: 最外层遍历数组的时候,已经算过的target存入字典中,下次碰到不再计算,否则会卡case 322/323,一个全是[0,0,0,0...]的case
如果miss掉优化点1和2会卡3个case,miss掉优化3会卡最后2个case
class Solution:
def threeSum(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
if not nums or len(nums)<3:
return []
nums.sort()
res = set()
d_key = {} # already as target,consider only once
for i in range(len(nums)):
target = nums[i]
d = {}
if target in d_key:
continue
for j in nums[i+1:]:
if -target - j in d:
res.add((target,-target-j,j))
else:
d[j] = 1
d_key[target] = 1
return [list(i) for i in res] # map(list,res)
解法2:双指针方法
leetcode讨论区看到的,和快排的while比较像
即遍历当前列表,到第i位置处,当前位置处的值为target=nums[i]
左指针在下一位lp = nums[i+1],右指针在最右边nums[len(nums)-1]
比如[-1,-1,0,1,2,4] 定位到第一个-1, left = -1, right =2 是一个解,之后左右指针各往里移动一位,0,1又是一个解,所以返回两个解
class Solution:
def threeSum(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
if not nums or len(nums) < 3 :
return []
nums.sort()
res = set()
for i in range(len(nums)-2):
if i >=1 and nums[i] == nums[i-1]: # 和使用d={}存已经计算过的元素一样
continue
target = nums[i]
lp = i+1
rp = len(nums)-1
s = target + nums[lp] + nums[rp]
while lp < rp :
s = target + nums[lp] + nums[rp]
if s < 0 : # s < 0 means lp go right
lp += 1
elif s > 0 :
rp -= 1
else:
res.add((target,nums[lp],nums[rp]))
lp += 1
rp -= 1
while lp < rp and nums[lp ] == nums[lp -1]: # 碰到重复元素的话继续往前
lp += 1
while lp < rp and nums[rp] == nums[rp+1]:
rp -= 1
return [list(i) for i in res]
3. 3-Sums closest
题目描述:在一个数组中找到3个数,要求3个数的和与目标target最接近
Given an array S of n integers, find three integers in S such that the sum is closest to a given number, target. Return the sum of the three integers. You may assume that each input would have exactly one solution.
For example, given array S = {-1 2 1 -4}, and target = 1.
The sum that is closest to the target is 2. (-1 + 2 + 1 = 2).
class Solution:
def threeSumClosest(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: int
"""
if not nums or len(nums) < 3 :
return []
nums.sort()
res = set()
closest = nums[0]+ nums[1] + nums[2]
max_error = abs(nums[0]+ nums[1] + nums[2] - target)
for i in range(len(nums)-2):
if i >=1 and nums[i] == nums[i-1]:
continue
base = nums[i]
lp = i+1
rp = len(nums)-1
s = base + nums[lp] + nums[rp]
while lp < rp :
s = base + nums[lp] + nums[rp]
if abs(s - target) < max_error:
max_error = abs(s - target)
closest = s
if s < target : # s < target means lp go right
lp += 1
elif s > target :
rp -= 1
else:
return target
return closest
4. 4SUMs
和3SUMS 类似,给定数组和target,求4个数的和等于target的数组
可以看做3SUMS的扩展,遍历数组,用target - i 当做3SUM问题的target
上面的3sum是求和为0的3sum,如果改为和为target的,只需加一个参数,然后双指针移动的时候,考虑s与target的相对大小
s < target: lp += 1
s > target: rp += 1
56. Merge Intervals
Given a collection of intervals, merge all overlapping intervals.
For example,
Given [1,3],[2,6],[8,10],[15,18],
return [1,6],[8,10],[15,18].
对重合的区间进行merge
首先把二维数组根据第一个元素进行排序:
python里sorted(nums,key=lambda i: i[0])即按照第一个元素排序
本题定义了数据结果,interval由start和end组成,因此sorted(inter,key=lambda i: i.start)
比较两个区间a,b的方法是,如果第一个区间的end元素比第二个区间的start还要小,那两个都保留到结果里,
否则,新区间变为[ min(a[0],b[0]), max(a[1],b[1])]
这里要注意,遍历原来的数组时,每个区间要和之前合并好的区间里最后那个比
# Definition for an interval.
# class Interval(object):
# def __init__(self, s=0, e=0):
# self.start = s
# self.end = e
class Solution(object):
def merge(self, intervals):
"""
:type intervals: List[Interval]
:rtype: List[Interval]
"""
if not intervals:
return []
intervals = sorted(intervals, key=lambda i: i.start)
out = [intervals[0]] # result list
for i in range(1,len(intervals)):
a = intervals[i] # compare this index and last element of result list
b = out[-1]
if b.end < a.start:
out.append(a)
else:
# out[-1] = Interval(min(a.start,b.start),max(a.end,b.end))
# 这里最好直接更改out[-1]的end元素,不要再创建一个对象,否则效率太低
out[-1].end = max(a.end,b.end)
return out
57. Insert Interval
第57道,插入区间,插入完和前后的区间merge,可以用和上面完全一样的函数,只是输入变成
intervals = intervals + [newinterval]
AC, beat 80%
剑指offer36 数组中的逆序对
在数组中,两个元素,如果前面一个大于后面那个的数字,则形成一个逆序对,输入一个数组,求出所有这个数组中逆序对的总数。
代码基本和归并排序一样,需要计数的地方在cc += len(left) - i 里
left 和 right 都为排序好的升序数组,要merge到一起,当前指针为i,j
如果left[i] <= right[j]: 没有问题,不构成逆序对
如果left[i] > right[j]: 此时merge时要插入right[j]的值,可见right[j]小于left[i]
则right[j]小于 left[ i:] left从i到最后的所有元素。
因此要加 len(left) - i 个逆序对
def reversePair(lst):
global cc
if len(lst) <=1:
return lst
else:
middle = len(lst)//2
left = lst[:middle]
right = lst[middle:]
merge_sort(left)
merge_sort(right)
i,j,k = 0,0,0
while i < len(left) and j< len(right):
if left[i] <= right[j]:
lst[k] = left[i]
i = i + 1
else:
lst[k] = right[j]
cc += len(left) - i # <- 此处为和归并排序唯一区别
# print(len(left) -i)
j = j + 1
k = k + 1
while i < len(left):
lst[k] = left[i]
i += 1
k += 1
while j < len(right):
lst[k] = right[j]
j += 1
k += 1
return lst
if __name__ == '__main__':
lst = [[1,2,3,4,7,6,5],[6,5,4,3,2,1],[1,2,3,4,5,6],[1],[1,2],[2,1],[1,2,1,2,1],[1,7,4,5,3,8,2]]
for i in lst:
cc = 0
reversePair(i)
print(cc)