Leetcode“最经典”算法题之一:三数之和

496 阅读3分钟

这是我参与2022首次更文挑战的第9天,活动详情查看:2022首次更文挑战

一、写在前面

LeetCode 第一题两数之和传输门:听说你还在写双层for循环解两数之和?

LeetCode 第二题两数之和传输门:两个排序数组的中位数,“最”有技术含量的解法!

LeetCode 第三题最长回文子串传输门:马拉车算法解最长回文子串!Manacher

LeetCode 第四题字符串转整数 (atoi):“愚公移山”的方法解atoi,自以为巧妙!

LeetCode 第五题最长公共前缀:继续解Leetcode,最长公共前缀

今天给大家分享的是LeetCode 数组与字符串 第六题:三数之和,为面试而生,期待你的加入。

二、今日题目

给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。

注意: 答案中不可以包含重复的三元组。

示例:

例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4],

满足要求的三元组集合为:
[
  [-1, 0, 1],
  [-1, -1, 2]
]

三、 分析

看到这个题目第一眼,我想到的就是两数之和,咋一眼感觉上没有太大区别,实际是二维空间和三维空间的区别。

最开始想的是和两数之和方法类似,这里我只要找到两数之和的相反数是不是在列表就行了,但真的实践起来,比较麻烦,时间复杂度先不说,就是思想上随便一说都是个麻烦事情,所以X先生果断放弃了这样掉头发不讨好的方法,换了一种思想,头+尾搜索法:头部循环,尾部从两端向中间搜索,思想如下:

我的思想

四、解题

  • 我的方法: 从从中心向外扩散,时间复杂度:小于O(n^2)
class Solution(object):
    def threeSum(self, nums):
		"""
		:type nums: List[int]
		:rtype: List[List[int]]
		"""
		# 列表排序,从小到大
		nums.sort()
		# [-4, -1, -1, 0, 1, 2]
		res_list = []
		# 头部循环查找
		for i in range(len(nums)):
			if i == 0 or nums[i] > nums[i - 1]:
				# 最左端
				l = i + 1  
				# 最右端
				r = len(nums) - 1
				while l < r:  # 正在查找
					three_sum = nums[i] + nums[l] + nums[r]
					if three_sum == 0:
						res_list.append([nums[i], nums[l], nums[r]])
						l += 1 # 右移一位
						r -= 1 # 左移一位
						while l < r and nums[l] == nums[l - 1]:
							# 从左往右,相同数值直接跳过
							l += 1
						while r > l and nums[r] == nums[r + 1]:
							# 从右往左,相同数值直接跳过
							r -= 1
					elif three_sum > 0:
						# 大于零,右边数值大,左移
						r -= 1
					else:
						# 小于零,左边数值小,右移
						l += 1
		return res_list
  • 提交结果

提交结果

测试数据:313组
运行时间:740ms
击败人百分比:81.35%

五、疑惑

这里说一下上一篇最长公共前缀大牛方法中的zip()。

zip为Python内置函数,直接调用即可,其作用是将参数(序列类型,字符串、元祖、列表)按遍历顺序一一转变成元组,如:

a = 'sttd'
b = 'stba'
c = 'sttmss'

for i in zip(a,b,c):
    print(i)

image.png

在上一题场景中,我们只需遍历这里的每一个元组内元素是否相等即可,相等就说明是相同前缀,直到遍历到不等,就可以返回前面的值了。

如上图中,遍历到('t', 'b', 't')时,三个元素不同,所以a b c最长公共前缀为st。

六、结语

坚持 and 努力 : 终有所获。

思想很复杂,

实现很有趣,

只要不放弃,

终有成名日。

—《老表打油诗》

下期见,我是爱猫爱技术的老表,如果觉得本文对你学习有所帮助,欢迎点赞、评论、关注我!