首先是这个454 三数相加
class Solution(object):
def threeSum(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
nums.sort()
list = []
for i in range(len(nums)):
if i > 0 and nums[i] == nums[i - 1]:
continue
for j in range(i + 1, len(nums)):
if j > i + 1 and nums[j] == nums[j - 1]:
continue
for k in range(j + 1, len(nums)):
if k > j + 1 and nums[k] == nums[k - 1]:
continue
# print(i, j, k)
if nums[i] + nums[j] + nums[k] == 0:
list.append([nums[i], nums[j], nums[k]])
return list
if __name__ == '__main__':
s = Solution()
print(s.threeSum(nums = [0,0,0,0]))
这种O(n3)的写法是肯定会被干的,贴一下这种羞耻的写法,那么这时候你就有幸看到传说中的308样例:),那有的人可能会觉得删除自己写出来的金碧辉煌的屎山极度不舍,比如我昨天用列表写了个二层感知机(因为还没正式教torch,提前用感觉不太好),损失函数用的是交叉熵,一直收敛到一个错误结果上,这下这下了。于是我把第三个j的遍历用二分查找代替,那复杂度应该就是O(n2logn),只能说勉强AC,通过时间排在最后,同样的贴出我的代码
class Solution(object):
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
for j in range(i + 1, len(nums)):
if j > i + 1 and nums[j] == nums[j - 1]:
continue
target = -nums[i] - nums[j]
loc = self.my_sort(nums, j + 1, len(nums) - 1, target)
if loc > 0:
res.append([nums[i], nums[j], nums[loc]])
return res
def my_sort(self, nums, left, right, target):
while left <= right:
mid = (left + right) // 2
if nums[mid] == target:
return mid
if nums[mid] < target:
left = mid + 1
else:
right = mid - 1
return -1
if __name__ == '__main__':
s = Solution()
print(s.threeSum(nums = [0,0,0]))
那之所以我贴出了On3和On2logn的代码都是为了把这两份屎山从我的硬盘中清除,留下卡哥的代码,下面贴出来自卡哥的代码
将第一个元素从头刷到尾,双指针作为左右窗棱慢慢收拢
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
result = []
nums.sort()
for i in range(len(nums)):
# 如果第一个元素已经大于0,不需要进一步检查
if nums[i] > 0:
return result
# 跳过相同的元素以避免重复
if i > 0 and nums[i] == nums[i - 1]:
continue
left = i + 1
right = len(nums) - 1
while right > left:
sum_ = nums[i] + nums[left] + nums[right]
if sum_ < 0:
left += 1
elif sum_ > 0:
right -= 1
else:
result.append([nums[i], nums[left], nums[right]])
# 跳过相同的元素以避免重复
while right > left and nums[right] == nums[right - 1]:
right -= 1
while right > left and nums[left] == nums[left + 1]:
left += 1
right -= 1
left += 1
return result
注意
1、最后的else当中,以下这种写法也是可以的
else:
result.append([nums[i], nums[left], nums[right]])
right -= 1
left += 1
# 跳过相同的元素以避免重复
while right > left and nums[right] == nums[right + 1]:
right -= 1
while right > left and nums[left] == nums[left - 1]:
left += 1
2、为何结尾不需要return res?
因为i会扫过所有的元素(在不碰到大于0的元素之前),一碰到大于0的元素就返回[]了
卡哥的代码非常简洁,把所有的判断条件都融合得非常完美(如果你不只是在脑子里过了一下而是动手写了的话)
特别强调的一点,在出现访问数组的时候就得判断是否访问越界,也就是要控制访问条件,这就是为什么有的地方要填上left<right有的地方可以省
18 四数之和与15 三数之和都是将n次方的算法变成n-1次方的算法,同理可得k数之和可以将k次方的算法变成k-1次方的算法,方法都是最后的2次在排序情况下可以通过左右窗棱的滑动用一次解决