轻松拿下两数、三数、四数和N数之和 | 面试必备算法

简介:

两数之和

题目

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出和为目标值的那两个整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。

示例 1:

输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。

示例 2:

输入:nums = [3,2,4], target = 6
输出:[1,2]

示例 3:

输入:nums = [3,3], target = 6
输出:[0,1]

提示:

2 <= nums.length <= 103
-109 <= nums[i] <= 109
-109 <= target <= 109
只会存在一个有效答案

解题

本题可以通过三种方式去解答

暴力枚举

  • 最容易想到的方法是双层遍历枚举nums,查询 nums[i] + nums[j] = target

算法源码:

class Solution(object):
    def twoSum(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: List[int]
        """
        # 1. 暴力
        for i in range(len(nums) - 1):
            for j in range(i + 1, len(nums)):
                if nums[i] + nums[j] == target:
                    return [i, j]
        return []

时间复杂度:O(N^2),其中N是数组中的元素数量。最坏情况下数组中所有数都需要被匹配

空间复杂度:O(1)

排序 + 双指针

  • 查询nums[i] + nums[j] = target还很容易想到双指针,但双指针前提需要有序数组,故先对数组进行排序并且保留原始索引
nums = [[i, v] for i, v in enumerate(nums)]
nums = sorted(nums, key=lambda k: k[1], reverse=False)

算法源码:

class Solution(object):
    def twoSum(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: List[int]
        """
        # 2. 排序+双指针
        nums = [[i, v] for i, v in enumerate(nums)]
        nums = sorted(nums, key=lambda k: k[1], reverse=False)
        start, end = 0, len(nums) - 1
        while start < end:
            tmp_sum = nums[start][1] + nums[end][1]
            if tmp_sum < target:
                start += 1
            elif tmp_sum > target:
                end -= 1
            else:
                return [nums[start][0], nums[end][0]]
        return []

时间复杂度:O(NlogN),其中先将数组排序好O(NlogN),再双指针遍历O(N)得到结果

空间复杂度:O(N),其中N是数组中的元素数量

哈希

  • 上述方法查询target - x时间复杂度较高,因此,为了更快速查询数组中是否有目标元素,我们可以使用索引。使用哈希表,可以将寻找target - x的时间复杂度降低从O(N)降低到O(1)

算法源码:

class Solution(object):
    def twoSum(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: List[int]
        """
        # 3. 哈希
        hash_res = {}
        for k, v in enumerate(nums):
            if target - v in hash_res:
                return [hash_res[target - v], k]
            hash_res[v] = k
        return []

时间复杂度:O(N),其中N是数组中的元素数量。对于每一个元素 x,我们可以O(1)地寻找 target - x

空间复杂度:O(N),其中N是数组中的元素数量。主要为哈希表的开销

三数之和

题目

给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。

示例 1:

输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]

示例 2:

输入:nums = []
输出:[]

示例 3:

输入:nums = [0]
输出:[]

提示:

0 <= nums.length <= 3000
-105 <= nums[i] <= 105

解题

排序 + 迭代1次 + 双指针

  • 该题核心是nums[k] + nums[i] + nums[j] = target公式,假如固定nums[k]很容易联想到两数之和(排序 + 双指针)解法。
原列表:[-1,0,1,2,-1,-4]
排序后:[-4,-1,-1,0,1,2]
         ^  ^  ^
     固定k,  i,j 双指针解法
  • 故可以通过外层加一次循环遍历改造排序+双指针的解法,伪代码如下
for k, v in enumerate(nums[:-2]):
#    双指针解法
  • 注意本题临界条件len(nums) < 3判断和三元组不重复判断

算法源码:

class Solution(object):
    def threeSum(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        if len(nums) < 3: return [] # 临界判断
        nums = sorted(nums, reverse=False)
        res = set()                 # 去重                         
        for i, v in enumerate(nums[:-2]):
            if v > 0: break         # 剪枝优化
            start, end = i + 1, len(nums) - 1
            while start < end:
                if v + nums[start] + nums[end] > 0:
                    end -= 1
                elif v + nums[start] + nums[end] < 0:
                    start += 1
                else:
                    temp = (v, nums[start], nums[end])
                    # temp = sorted(temp, reverse=False)  # 排序去除重复
                    res.add((temp[0], temp[1], temp[2]))
                    start += 1
        return list(res)

时间复杂度:O(N^2),排序O(NlogN) + 查询比较O(N^2)

空间复杂度:O(N),其中N是数组中的元素数量

四数之和

题目

给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。
注意:答案中不可以包含重复的四元组。

示例 1:

输入:nums = [1,0,-1,0,-2,2], target = 0
输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]

示例 2:

输入:nums = [], target = 0
输出:[]

提示:

0 <= nums.length <= 200
-109 <= nums[i] <= 109
-109 <= target <= 109

解题

排序 + 迭代2次 + 双指针

  • 该题核心是nums[k] + nums[m] + nums[i] + nums[j] = target公式,假如固定nums[k]nums[m]很容易联想到两数之和(排序 + 双指针)和三数之和的解法。
  • 故可以通过外层加一次循环遍历改造三数之和的解法,伪代码如下
for k, v in enumerate(nums[:-3]):
#    三数之和解法

算法源码:

class Solution(object):
    def fourSum(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: List[List[int]]
        """
        if len(nums) < 4: return []
        nums = sorted(nums, reverse=False)
        res = set()            # 去重
        for i, i_v in enumerate(nums[:-3]):
            for j, j_v in enumerate(nums[i + 1:-2]):
                start, end = i + j + 2, len(nums) - 1
                while start < end:
                    sum_val = i_v + j_v + nums[start] + nums[end]
                    if sum_val > target:
                        end -= 1
                    elif sum_val < target:
                        start += 1
                    else:
                        temp = [i_v, j_v, nums[start], nums[end]]
                        # temp = sorted(temp, reverse=False)
                        res.add((temp[0], temp[1], temp[2], temp[3]))
                        start += 1
        return list(res)

时间复杂度:O(N^3),排序O(NlogN) + 查询比较O(N^3)

空间复杂度:O(N),其中N是数组中的元素数量

N数之和

排序 + 递归迭代N-1次 + 双指针

算法代码:

class Solution(object):
    def fourSum(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: List[List[int]]
        """
        # 2.通用递归方法
        def nSum(nums, n, target):
            if len(nums) < n: return []
            res = []
            if n == 2:
                left, right = 0, len(nums)-1
                while left < right:
                    if nums[left] + nums[right] == target:
                        res.append([nums[left], nums[right]])
                        while left < right and nums[left] == nums[left+1]: left += 1
                        while left < right and nums[right] == nums[right-1]: right -= 1
                        left += 1
                        right -= 1
                    elif nums[left] + nums[right] > target:
                        right -= 1
                    else:
                        left += 1
                return res
            else:
                for i in range(len(nums)):
                    if i > 0 and nums[i] == nums[i - 1]: continue
                    sub_res = nSum(nums[i + 1:], n - 1, target - nums[i])
                    for j in range(len(sub_res)): res.append([nums[i]] + sub_res[j])
                return res

        if len(nums) < 4:   return []
        nums = sorted(nums, reverse=False)
        return nSum(nums, 4, target)

总结

  • N数之和可通过最后模版代码通用处理,但由于整个核心是通过双指针减少一层遍历,故N值过大整体性能也受限

附录

❤️❤️❤️读者每一份热爱都是笔者前进的动力!

我是三十一,感谢各位朋友:点赞、收藏和评论,我们下期再见!

相关文章
|
3月前
|
负载均衡 NoSQL 算法
一天五道Java面试题----第十天(简述Redis事务实现--------->负载均衡算法、类型)
这篇文章是关于Java面试中Redis相关问题的笔记,包括Redis事务实现、集群方案、主从复制原理、CAP和BASE理论以及负载均衡算法和类型。
一天五道Java面试题----第十天(简述Redis事务实现--------->负载均衡算法、类型)
|
3月前
|
算法 Go
[go 面试] 雪花算法与分布式ID生成
[go 面试] 雪花算法与分布式ID生成
|
1月前
|
算法 Java 数据库
美团面试:百亿级分片,如何设计基因算法?
40岁老架构师尼恩分享分库分表的基因算法设计,涵盖分片键选择、水平拆分策略及基因法优化查询效率等内容,助力面试者应对大厂技术面试,提高架构设计能力。
美团面试:百亿级分片,如何设计基因算法?
|
1月前
|
算法 前端开发 Java
数据结构与算法学习四:单链表面试题,新浪、腾讯【有难度】、百度面试题
这篇文章总结了单链表的常见面试题,并提供了详细的问题分析、思路分析以及Java代码实现,包括求单链表中有效节点的个数、查找单链表中的倒数第k个节点、单链表的反转以及从尾到头打印单链表等题目。
33 1
数据结构与算法学习四:单链表面试题,新浪、腾讯【有难度】、百度面试题
|
29天前
|
机器学习/深度学习 算法 Java
机器学习、基础算法、python常见面试题必知必答系列大全:(面试问题持续更新)
机器学习、基础算法、python常见面试题必知必答系列大全:(面试问题持续更新)
|
1月前
|
算法 Java 数据库
美团面试:百亿级分片,如何设计基因算法?
40岁老架构师尼恩在读者群中分享了关于分库分表的基因算法设计,旨在帮助大家应对一线互联网企业的面试题。文章详细介绍了分库分表的背景、分片键的设计目标和建议,以及基因法的具体应用和优缺点。通过系统化的梳理,帮助读者提升架构、设计和开发水平,顺利通过面试。
美团面试:百亿级分片,如何设计基因算法?
|
1月前
|
算法 Java 数据中心
探讨面试常见问题雪花算法、时钟回拨问题,java中优雅的实现方式
【10月更文挑战第2天】在大数据量系统中,分布式ID生成是一个关键问题。为了保证在分布式环境下生成的ID唯一、有序且高效,业界提出了多种解决方案,其中雪花算法(Snowflake Algorithm)是一种广泛应用的分布式ID生成算法。本文将详细介绍雪花算法的原理、实现及其处理时钟回拨问题的方法,并提供Java代码示例。
72 2
|
2月前
|
机器学习/深度学习 JavaScript 算法
面试中的网红虚拟DOM,你知多少呢?深入解读diff算法
该文章深入探讨了虚拟DOM的概念及其diff算法,解释了虚拟DOM如何最小化实际DOM的更新,以此提升web应用的性能,并详细分析了diff算法的实现机制。
|
3月前
|
消息中间件 存储 算法
这些年背过的面试题——实战算法篇
本文是技术人面试系列实战算法篇,面试中关于实战算法都需要了解哪些内容?一文带你详细了解,欢迎收藏!
|
3月前
|
JavaScript 算法 索引
【Vue面试题二十三】、你了解vue的diff算法吗?说说看
这篇文章深入分析了Vue中的diff算法,解释了其在新旧虚拟DOM节点比较中的工作机制,包括同层节点比较、循环向中间收拢的策略,并通过实例演示了diff算法的执行过程,同时提供了源码层面的解析,说明了当数据变化时,如何通过Watcher触发patch函数来更新DOM。
【Vue面试题二十三】、你了解vue的diff算法吗?说说看
下一篇
无影云桌面