力扣(LeetCode)通往百万年薪之路--第1题 两数之和

本文详细解析了LeetCode上的两数之和问题,提供了线性查找和哈希表两种解决方案,分析了它们的时间复杂度和空间复杂度。线性查找法时间复杂度为O(N^2),哈希表法则降低到O(N)。通过哈希表预处理,实现了快速查找目标值,优化了空间换时间的策略。并给出了C、C++和Java的实现代码,以及如何举一反三,应用于其他类似问题。
摘要由CSDN通过智能技术生成

1、两数之和:

给定一个整数数组 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

只会存在一个有效答案

 

【数据结构与算法】知识点提取:线性查找-数组,哈希表

 

方法一:【线性查找  0(n) 】数组枚举法,使用两个for循环遍历数组,数组中每个元素与其后面每个元素依次相加,判断是否等于target。

c实现:注意该函数返回后记得释放内存free,防止内存泄漏。

int* twoSum(int* nums, int numSize, int target, int* returnSize)
{
    if(nums == NULL || numSize == 0)
    {
        *returnSize = 0;
        return NULL;
    }
    for(int i = 0; i < numSize; i++)
    {
        for(int j = i+1; j < numSize; j++)
        {
            if(nums[i] + nums[j] == target)
            {
                int *ret = malloc(sizeof(int)*2);
                ret[0] = i;
                ret[1] = j;
                *returnSize = 2;
                return ret;
            }
        }
    }

    *returnSize = 0;
    return NULL;

}

c++实现:

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        int n = nums.size();
        for (int i = 0; i < n; ++i) {
            for (int j = i + 1; j < n; ++j) {
                if (nums[i] + nums[j] == target) {
                    return {i, j};
                }
            }
        }
        return {};
    }
};

 

复杂度分析: 

时间复杂度:0(N^2),其中N是数组中的元素数量。最坏情况下数组中任意两个数都要被匹配一次。

空间复杂度:0(1)。

 

方法二:【哈希表】

思路及算法

注意到方法一的时间复杂度较高的原因是寻找 target - x 的时间复杂度过高。因此,我们需要一种更优秀的方法,能够快速寻找数组中是否存在目标元素。如果存在,我们需要找出它的索引。使用哈希表,可以将寻找 target - x 的时间复杂度降低到从 O(N^2) 降低到 O(1)。这样我们创建一个哈希表,对于每一个 x,我们首先查询哈希表中是否存在 target - x,然后将 x 插入到哈希表中,即可保证不会让 x 和自己匹配。

c实现:

struct hashTable
{
    int key;
    int val;
    UT_hash_handle hh;
}

struct hashTable* find(int ikey)
{
    struct hashTable* tmp;
    HASH_FIND_INT(hashTable, &ikey, tmp);
    return tmp;    
}

void insert(int ikey, int ival)
{
    struct hashTable* it = find(ikey);
    if(it == NULL)
    {
        struct hashTable* tmp = malloc(sizeof(struct hashTable));
        tmp->key = ikey;
        tmp->val = ival;
        HASH_ADD_INT(hashtable, ikey , tmp);
    }
    else
    {
        it->val = ival;
    }

}


int* twoSum(int* nums, int numSize, int target, int* returnSize)
{

   if(nums == NULL || numSize == 0)
   {
        *returnSize = 0;
        return NULL;
   }

   hashtable = NULL; 
   for(int i = 0; i < numSize; i++)
    {
        struct hashTable * it = find(target - nums[i]);
        if(it != NULL)
        {
            int *ret = malloc(sizeof(int)*2);
            ret[0] = it->val;
            ret[1] = i;
            *returnSize = 2;
            return ret;     
        }
        intsert(nums[i],i);
    }

    *returnSize = 0;
    return NULL;
}

c++实现:

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map<int, int> hashtable;
        for (int i = 0; i < nums.size(); ++i) {
            auto it = hashtable.find(target - nums[i]);
            if (it != hashtable.end()) {
                return {it->second, i};
            }
            hashtable[nums[i]] = i;
        }
        return {};
    }
};

 

java实现:

// 时间复杂度:O(n)
// 空间复杂度:O(n)
// 空间换时间
public int[] twoSum(int[] nums, int target) {
    if (nums == null || nums.length == 0) return new int[0];

    // 数据预处理
    HashMap<Integer, Integer> map = new HashMap<>();
    for (int i = 0; i < nums.length; i++) { // O(n)
        map.put(nums[i], i);
    }

    for (int i = 0; i < nums.length; i++) { // O(n)
        int x = nums[i];
        // 哈希查找 - O(1)
        if (map.containsKey(target - x)) {
            int index = map.get(target - x);
            // i 和 index 不是同一个元素,同一个元素不能使用两次
            if (i != index) return new int[]{i, index};
        }
    }

    return new int[0];
}

 

java实现优化:

// 时间复杂度:O(n)
// 空间复杂度:O(n)
// 空间换时间
public int[] twoSum(int[] nums, int target) {
    if (nums == null || nums.length == 0) return new int[0];
    int n = nums.length;

    Map<Integer, Integer> map = new HashMap<>();
    for (int i = 0; i < n; i++) { // O(n)
        int x = nums[i];
        // 哈希查找
        if (map.containsKey(target - x)) {
            int index = map.get(target - x);
            return new int[]{i, index};
        }
        map.put(x, i);
    }
    return new int[0];
}

 

复杂度分析

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

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

 

现在你再刷下面的 5 道类似题目:(举一反三,总结知识点,反复思考)

leetcode 167 号算法题:两数之和Ⅱ - 输入有序数组

leetcode 170 号算法题:两数之和Ⅲ - 数据结构设计
leetcode 653 号算法题:两数之和Ⅳ - 输入 BST
leetcode 15 号算法题:三数之和
leetcode 18 号算法题:四数之和

首先, 同学们不要为了刷题而刷题,要掌握核心该题知识点,做到举一反三。平时练习基本思路:碰到一个算法, 不知道最优解的话, 先用最简单、最暴力的解法-->
找到暴力解法的性能瓶颈,-->进行优化,不断的优化, 优化的方法可能有好几个, 你可以都进行分析-->选择一个最优的解法, 看看还能不能继续优化, 在追求程序极致性能的过程中, 你的功力会慢慢提高的,做算法题的效率也会提高,自信心也就建立了。最后总结反思,并构建自己的【数据结构与算法】知识体系。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Linux技术芯

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值