力扣--数组类题目1.两数之和

1.两数之和

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 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]
方法一、暴力解法

时间复杂度:O(n的平方 ),空间复杂度:O(1)

该解法即对numsnumsnums进行双重遍历,将所有两个数组合情况均列举一遍,若有符合题意情况直接返回即可

python 复制代码
from typing import List
def twoSum(nums: List[int], target: int) -> List[int]:
    for i in range(len(nums)):
        for j in range(i+1,len(nums)):
            if nums[i]+nums[j]==target:
                return [i,j]
nums=[2,7,11,15]
target = 17
a=twoSum(nums,target)
print(a)#[0, 3]

方法二、二分查找法

时间复杂度:O(nlogn),空间复杂度:O(n)

在暴力解法中,第二层遍历是对iii后面的元素进行无差别枚举,这样会导致非常多的无效枚举。

我们可以借助二分算法,来对内层循环从时间上进行优化。

因为二分算法的前提是"有序",则我们需要先对numsnumsnums进行排序。

又因为返回答案需要原numsnumsnums顺序,所以我们不妨提前将numsnumsnums中的值与其下标做一个绑定,统一排序

python 复制代码
import bisect
from typing import List

def twoSum(nums: List[int], target: int) -> List[int]:
    n = len(nums)
    #print(list(zip(nums, range(n))))#[(2, 0), (11, 1), (7, 2), (15, 3)]
    nums = sorted(zip(nums, range(n))) # O(nlogn)
    print(nums)#[(2, 0), (7, 1), (11, 2), (15, 3)]
    # 以下部分时间复杂度为:O(nlogn)
    for i in range(n):
        t = target - nums[i][0]
        print("t is:",t)
        j = bisect.bisect_left(nums, (t, ), i + 1, n)#见如下备注
        print("j is:",j)
        if j < n and nums[j][0] == t:
            return [nums[i][1], nums[j][1]]#[1, 3]
    return []

nums=[2,11,7,15]
target = 26
a=twoSum(nums,target)
print(a)
# t is: 24 备注 t为24然后在nums中查找24从i为1查到3则查到24在nums中处于第4位置所以j为4
# t is: 19 备注 t为19然后在nums中查找19从i为2查到3则查到19在nums中处于第4位置所以j为4
# t is: 15 备注 t为15然后在nums中查找15从i为3查到3则查到15在nums中处于第3位置所以j为3
# [1, 3]

方法三、双指针

时间复杂度:O(nlogn),空间复杂度:O(n)

在上面二分查找的优化中,可发现"有序"可以极大的优化内层循环的时间复杂度

发散思维,摒弃内外层循环的思路,"有序"是否还可以发挥更大的作用呢?

答案是可以的,即使用双指针算法

python 复制代码
from typing import List

def twoSum(nums: List[int], target: int) -> List[int]:
    n = len(nums)
    nums = sorted(zip(nums, range(n)))  # O(nlogn)#一定要排序要不然可能返回不了正确的结果
    print(nums)#[(2, 0), (6, 6), (7, 2), (9, 7), (11, 1), (12, 5), (15, 3), (26, 4)]
    # 以下部分时间复杂度为:O(n)
    left, right = 0, n - 1
    while left < right:
        if nums[left][0] + nums[right][0] == target:
            return [nums[left][1], nums[right][1]]
        elif nums[left][0] + nums[right][0] > target:
            right -= 1
        else:
            left += 1
    return []

nums=[2,11,7,15,26,12,6,9]
target = 20
# nums=[2,5,1,4,7,3,33,20,9]
# target= 36#如果不排序则返回不了正确的结果
a=twoSum(nums,target)
print(a)
#总结:排序后为[(2, 0), (6, 6), (7, 2), (9, 7), (11, 1), (12, 5), (15, 3), (26, 4)]
#从小到大排好后首先是2+26=28,28大于20则right-1,因为最小的数加上最大的数>target,则这里最大数28舍弃
#因为28和最小的相加都大于20,和其它数相加更大于20,所以right-1
#接下来就是2+15=17,17小于target,所以要找更大的数和15相加,所以left-1

方法四,哈希表解法

时间复杂度:O(n),空间复杂度:O(n)

1.字典是一种典型的键值对类型的数据结构,每一个元素都是由一个键值对(键key和值value)组成

2.这种数据结构可以通过某个键来访问元素,所以字典也被称为映射或散列表

3.字典的主要特性是根据键快速查找值,也可以自由添加和删除元素,这有点像List,但跟List不同的是,List是连续存储,直接定址的。 字典像链表,非连续存储,所以删除元素不需要移动后续元素,就没有在内存中移动后续元素的性能开销

4.通过键来检索值的速度是非常快的,接近于 O(1),这是因为 Dictionary 类是作为一个哈希表来实现的,Dictionary 没有顺序之分,这一点不同于list列表,有顺序之分

键必须是唯一的,而值不需要唯一

5.字典中的键和值都是object类型,所以可以是任何类型(比如:string、int、自定义类型等等)

python 复制代码
from typing import List

def twoSum(nums: List[int], target: int) -> List[int]:
    n = len(nums)
    mp = {}
    for i in range(n):
        t = target - nums[i]
        print("i为%d时,字典为:%s"%(i,mp))
        if t in mp:
            return [mp[t], i]
        # 存放nums[i]
        mp[nums[i]] = i#依次把列表放入字典,key为列表中的值,value为列表中值的下标
    return []

nums=[2,11,7,15,26,12,6,9]
target = 20
a=twoSum(nums,target)
print(a)#[1, 7]#i为7时,字典为:{2: 0, 11: 1, 7: 2, 15: 3, 26: 4, 12: 5, 6: 6}

方法五、集合视角分类讨论

时间复杂度:O(n),空间复杂度:O(n)

从集合的角度来看

我们构造两个集合:

aaa集合为nums中所有值构成的集合。

bbb集合为nums中所有值与target的距离值构成的集合(即target−nums[i])

视角一

假设答案有解,那么aaa集合与bbb集合的交集一定有值。

假设答案无解,那么有两种情况:

aaa集合与bbb集合的交集为空集

aaa集合与bbb集合的交集只有一个值,值为target的一半,并且nums中该值仅存在一个。

视角二

此外,因为题目保证一定有解,所以我们还可以再换一个视角。

从答案的构成上来看,可以分为两种情况:

第一种为nums中两个相同元素的和为target

第二种为nums中两个不同元素的和为target,比如列表[2,11,7,15] sum=30时

python 复制代码
from typing import List

def twoSum(nums: List[int], target: int) -> List[int]:
    # 分情况讨论
    # 情况1. nums中有两个相同的值,和为target
    a = target // 2#除以2,然后向下取整
    print(a)
    if target % 2 == 0 and nums.count(a) >= 2:#count计算列表中参数x出现的次数,这里的条件是出现了2次及以上
        return [nums.index(a), nums.index(a, nums.index(a) + 1)]
        #index获得参数x在列表中的位置,index的第二个参数表示起始位置,第3个参数默认为末位置

    # 情况2. nums中有两个不同的值,和为target
    #set(nums)把列表转为集合,并且去重,集合中不能有相同的数据
    s = set(nums) & set([target - x for x in nums])#
    #即{2,11,26,12,6,9}和{8, 9, 11, 14, 18, -6}取交集,返回一个集合
    if s != set():
        b = s.pop()#返回被删除的集合中一个元素
        return [nums.index(b), nums.index(target - b)]
    # 情况3. nums中没有满足题意的情况
    return []

nums=[2,11,26,12,6,9]
target = 20
a=twoSum(nums,target)
print(a)#[5, 1]
相关推荐
დ旧言~22 分钟前
【高阶数据结构】图论
算法·深度优先·广度优先·宽度优先·推荐算法
张彦峰ZYF26 分钟前
投资策略规划最优决策分析
分布式·算法·金融
The_Ticker42 分钟前
CFD平台如何接入实时行情源
java·大数据·数据库·人工智能·算法·区块链·软件工程
Lenyiin1 小时前
02.06、回文链表
数据结构·leetcode·链表
爪哇学长1 小时前
双指针算法详解:原理、应用场景及代码示例
java·数据结构·算法
Dola_Pan1 小时前
C语言:数组转换指针的时机
c语言·开发语言·算法
繁依Fanyi2 小时前
简易安卓句分器实现
java·服务器·开发语言·算法·eclipse
烦躁的大鼻嘎2 小时前
模拟算法实例讲解:从理论到实践的编程之旅
数据结构·c++·算法·leetcode
C++忠实粉丝2 小时前
计算机网络socket编程(4)_TCP socket API 详解
网络·数据结构·c++·网络协议·tcp/ip·计算机网络·算法
祁思妙想2 小时前
10.《滑动窗口篇》---②长度最小的子数组(中等)
leetcode·哈希算法