LeetCode第十六题: 掌握双指针技巧 最接近的三数之和 【python】

简介: LeetCode第十六题: 掌握双指针技巧 最接近的三数之和 【python】

👤作者介绍:10年大厂数据\经营分析经验,现任大厂数据部门负责人。

会一些的技术:数据分析、算法、SQL、大数据相关、python

欢迎加入社区作者专栏每日更新:

LeetCode解锁1000题: 打怪升级之旅

image.png

备注说明:方便大家阅读,统一使用python,带必要注释,公众号 数据分析螺丝钉 一起打怪升级

题目描述

给你一个长度为 n 的整数数组 nums 和 一个目标值 target。请你从 nums 中选出三个整数,使它们的和与 target 最接近。

返回这三个数的和。

假定每组输入只存在恰好一个解。

示例 1:
 
输入:nums = [-1,2,1,-4], target = 1
输出:2
解释:与 target 最接近的和是 2 (-1 + 2 + 1 = 2) 。
示例 2:
 
输入:nums = [0,0,0], target = 1
输出:0

算法比较

下表总结了几种解决“最接近的三数之和”问题的算法的优劣势以及它们的时间和空间复杂度

  • 时间复杂度表示算法执行的速度,或者说完成算法需要的时间。时间复杂度低的算法在处理大数据集时更有优势。
  • 空间复杂度表示算法在执行过程中需要的存储空间大小。空间复杂度低意味着算法对内存的占用较小。

解题思路

1. 暴力法

这是最直接的方法,即尝试数组中所有可能的三数组合,并找到和最接近目标值 target 的组合。这种方法简单易懂,但时间复杂度非常高。

  1. 首先定义了一个函数 threeSumClosest 接收一个数组 nums 和一个目标值 target
  2. 它使用三层嵌套循环来穷举所有可能的三个数字的组合,并计算这三个数的和。如果这个和更接近目标值 target,就更新 closest_sum 为这个和。
  3. 最后,函数返回最接近 target 的三数之和。

代码实现

def threeSumClosest(nums, target):
    closest_sum = float('inf')  # 设置一个非常大的初始值
    min_diff = float('inf')  # 记录与target的最小差值
    
    # 对每个数进行遍历,计算所有可能的三数之和
    for i in range(len(nums)-2):
        for j in range(i+1, len(nums)-1):
            for k in range(j+1, len(nums)):
                sum = nums[i] + nums[j] + nums[k]
                diff = abs(target - sum)
                
                # 如果当前和更接近target,更新closest_sum
                if diff < min_diff:
                    min_diff = diff
                    closest_sum = sum
                    
    return closest_sum
 
# 示例
nums = [-1, 2, 1, -4]
target = 1
print(threeSumClosest(nums, target))

算法分析

  • 时间复杂度:O(n^3),因为需要三层循环遍历所有可能的组合。
  • 空间复杂度:O(1),因为只需要常数级别的额外空间。

2. 双指针法

  1. 排序:首先,我们需要对数组进行排序。排序是使用双指针技巧的前提,因为排序后才能通过指针的移动来控制和的大小。
  2. 双指针遍历:遍历数组,对于每个元素 nums[i],设置两个指针,一个指向 i+1,另一个指向数组的最末尾。通过比较三数之和与 target 的差值,移动指针来逼近目标值。
  3. 更新最接近值:在移动指针的过程中,如果遇到更接近目标值 target 的三数之和,就更新最接近值。

代码实现

def threeSumClosest(nums, target):
    nums.sort()
    closest_sum = float('inf')
    
    for i in range(len(nums) - 2):
        left, right = i + 1, len(nums) - 1
        while left < right:
            current_sum = nums[i] + nums[left] + nums[right]
            if abs(current_sum - target) < abs(closest_sum - target):
                closest_sum = current_sum
            
            if current_sum < target:
                left += 1
            elif current_sum > target:
                right -= 1
            else:
                return target  # 直接返回目标值
        
    return closest_sum

算法分析

  • 时间复杂度:排序的时间复杂度是 O(n log n),遍历数组并使用双指针的时间复杂度是 O(n^2),因此总体时间复杂度是 O(n^2)。
  • 空间复杂度:除了排序,我们只使用了常数额外空间,所以空间复杂度是 O(1)。

3. 排序加哈希表

这是一种折中的方法,将三数之和转换为两数之和的问题。对数组排序后,对于每个元素,使用哈希表快速查找是否存在满足条件的另外两个数。

解题思路

  1. 排序:首先对数组进行排序,这是为了后续能更高效地使用双指针技巧。
  2. 使用哈希表存储:考虑将每两个数的和以及这两个数的索引存储在哈希表中。这样做的目的是为了快速查找是否存在一个数,使得该数与某个和相加接近于目标值。
  3. 搜索和更新:遍历每个数,对于每个数,查找哈希表中是否存在与之配对的和,使得三数之和最接近于目标值,并更新最接近的和。

代码实现

def threeSumClosest(nums, target):
    nums.sort()  # 对数组进行排序
    closest_sum = float('inf')  # 初始化最接近的和为无穷大
    sums_hash = {}  # 初始化哈希表来存储两数之和和对应的索引
    
    # 填充哈希表
    for i in range(len(nums)):
        for j in range(i+1, len(nums)):
            two_sum = nums[i] + nums[j]
            # 哈希表的键为两数之和,值为这两个数的索引
            if two_sum not in sums_hash:
                sums_hash[two_sum] = []
            sums_hash[two_sum].append((i, j))
    
    # 遍历数组,使用哈希表查找最接近的和
    for k in range(len(nums)):
        for sum_val, indices in sums_hash.items():
            for index_pair in indices:
                if k not in index_pair:  # 确保不重复使用相同的元素
                    current_sum = sum_val + nums[k]
                    if abs(current_sum - target) < abs(closest_sum - target):
                        closest_sum = current_sum
                        
    return closest_sum

算法分析

  • 时间复杂度:O(n^2),对每个元素都可能需要一次线性时间的遍历。
  • 空间复杂度:O(n) 这种方法的空间复杂度较高,因为它存储了大量的两数之和和索引对。

4. 随机化算法

可以使用随机化技术,例如蒙特卡洛方法,随机选择三个数计算其和,多次迭代以提高找到最接近解的几率。

解题思路

  1. 随机选择:随机选择三个数,计算它们的和,并记录这个和与目标值target的差距。
  2. 迭代改进:重复上述过程多次,每次尝试时,如果找到了更接近目标值的和,就更新这个最接近的和。
  3. 停止条件:设定一个迭代次数作为停止条件,防止算法运行时间过长。

这种方法的优点是实现简单,能够在一定程度上探索解空间,但缺点是不能保证找到最优解,解的质量高度依赖于随机选择和迭代次数。

代码实现

import random
 
def threeSumClosest(nums, target):
    closest_sum = float('inf')  # 初始化最接近的和为无穷大
    iterations = 10000  # 设定迭代次数
    
    for _ in range(iterations):
        # 随机选择三个不同的索引
        indices = random.sample(range(len(nums)), 3)
        # 计算这三个数的和
        current_sum = sum(nums[i] for i in indices)
        # 如果这个和更接近target,更新closest_sum
        if abs(current_sum - target) < abs(closest_sum - target):
            closest_sum = current_sum
    
    return closest_sum
 
# 示例
nums = [-1, 2, 1, -4]
target = 1
print(threeSumClosest(nums, target))

算法分析

  • 时间复杂度:这取决于迭代次数,可能无法保证找到最优解。
  • 空间复杂度:O(1),不需要额外空间。

综合比较

这些方法与双指针方法相比,可能在特定情况下更加高效或简单。但从时间复杂度的角度看,双指针方法通常是最优的选择,尤其是对于大数据集。暴力法在实际应用中通常不可行,因为它的时间复杂度过高;二分查找法和哈希表方法在一些情况下可以提高效率,但它们要么需要额外的预处理步骤,要么需要额外的空间开销;随机化算法可能无法保证总是得到最接近的解。

在选择解题策略时,除了考虑时间和空间复杂度之外,还应该考虑实现的复杂性、代码的可读性以及算法的稳定性。在面试或竞赛中,双指针技术是一个常用且高效的解决方案,可以作为首选方法来解决类似的问题。


欢迎关注微信公众号 数据分析螺丝钉

相关文章
|
3月前
|
搜索推荐 索引 Python
【Leetcode刷题Python】牛客. 数组中未出现的最小正整数
本文介绍了牛客网题目"数组中未出现的最小正整数"的解法,提供了一种满足O(n)时间复杂度和O(1)空间复杂度要求的原地排序算法,并给出了Python实现代码。
113 2
|
3月前
|
算法
LeetCode第16题最接近的三数之和
该文章介绍了 LeetCode 第 16 题最接近的三数之和的解法,与第 15 题类似,通过双指针法减少循环次数,根据差值的绝对值来更新最接近的和,并总结了双指针可减少循环次数的要点。
|
3月前
|
算法 Python
【Leetcode刷题Python】 LeetCode 2038. 如果相邻两个颜色均相同则删除当前颜色
本文介绍了LeetCode 2038题的解法,题目要求在一个由'A'和'B'组成的字符串中,按照特定规则轮流删除颜色片段,判断Alice是否能够获胜,并提供了Python的实现代码。
50 3
|
3月前
|
Python
【Leetcode刷题Python】50. Pow(x, n)
本文介绍了LeetCode第50题"Pow(x, n)"的解法,题目要求实现计算x的n次幂的函数,文章提供了递归分治法的详细解析和Python实现代码。
26 1
|
3月前
|
Python
【Leetcode刷题Python】LeetCode 478. 在圆内随机生成点
本文介绍了LeetCode 478题的解法,题目要求在给定圆的半径和圆心位置的情况下实现在圆内均匀随机生成点的功能,并提供了Python的实现代码。
30 1
|
3月前
|
算法 Python
【Leetcode刷题Python】73. 矩阵置零
本文介绍了LeetCode第73题的解法,题目要求在给定矩阵中将所有值为0的元素所在的行和列全部置为0,并提供了一种原地算法的Python实现。
32 0
【Leetcode刷题Python】73. 矩阵置零
|
3月前
|
Python
【Leetcode刷题Python】1467. 两个盒子中球的颜色数相同的概率
本文介绍了LeetCode第50题"Pow(x, n)"的解法,题目要求实现计算x的n次幂的函数,文章提供了递归分治法的详细解析和Python实现代码。
39 0
|
算法 Python
leetcode-python经典题之一(二)
leetcode-python经典题之一(二)
leetcode-python经典题之一(二)
leetcode-python经典题之一
leetcode-python经典题之一
leetcode-python经典题之一