数据结构与算法:两数之和

1.题目:

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target  的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

你可以按任意顺序返回答案。

2.示例:

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

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

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

提示:

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

3.分析:

最简单的方法是暴力破解法,时间复杂度为O(n^2),这种方式非常简单,就是内外两个循环,这里不多做介绍。要想找到一种时间复杂度少于0(n^2)的方式,就要使用哈希算法,这里我介绍一个C语言的第三方库——uthash。它是用C语言实现的hash算法,它以宏定义的方式实现hash表,不仅加快了运行的速度,而且与key类型无关的优点。

用hash算法的解题思想:

为了方便说明,据如下例子:

数组:[2,11,7,15]   target:9=2+7

解题思想:

target=加数+加数

遍历数组,每次遍历时,本轮循环的数组元素作为一个加数,去寻找另一个加数。那另一个加数在哪里啦?它被保存到hash表格中。因此每轮循环就只有两个操作:

(1)在hash表格中寻找另一个加数。

(2)如果有,那target的两个加数就找到了。

(3)如果没有,就用本轮循环的数组元素的值和下标构成一个新的hash节点,把这个节点加入到hash表格中。

(4)进入下轮循环,重复(1)(2)(3),直到找到两个加数

4.代码:

#include"uthash.h"//这个头文件是要下载的,它就是用C语言实现的hash

struct hashTable {
    int key;//用来保存数组元素
    int val;//用来保存下标
    UT_hash_handle hh;//这个宏是必须要有的,我们后期不用它,但是如果要使用这个c语言的hash库,就要有这个宏,它相当于一个hash处理句柄
};

struct hashTable* hashtable;//定义一个哈希表格

//在hashtable表格中寻找是否有该数组元素值的节点。有,就返回该节点,反之返回NULL
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);//在hashTable中寻找是否已经存在该数组元素的节点,
    if (it == NULL) {//如果没有找到,就要构造一个新的节点,通过这个节点保存该数组元素的值和下标
        struct hashTable* tmp = malloc(sizeof(struct hashTable));//创建一个hashTable节点
        tmp->key = ikey;//保存该数组元素的值
        tmp->val = ival;//保存该数组元素的下标
        HASH_ADD_INT(hashtable, key, tmp);//把新创建的hashtable节点添加到已经存在的哈希表格中去
    } else {//如果在哈希表格中已经发现了一个与该数组元素相同的值的节点,那么只需要更新该节点的下标,不需要又添加一个新的节点来保存该数组元素值和下标了。这里是很明显的牺牲了时间复杂度,而提升了空间复杂度
        it->val = ival;//就记录这个数组的下标
    }
}

int* twoSum(int* nums, int numsSize, int target, int* returnSize) {
    hashtable = NULL;//对于指针变量,在使用之前最好明确赋值,否则容易出现错误
    for (int i = 0; i < numsSize; i++) {//遍历数组
        struct hashTable* it = find(target - nums[i]);//查找nums[i]+?=target的这个?值,这里用的是:减数=和-另一个减数
        if (it != NULL) {//判断建立的哈希表中是否有这个减数,如果有,则说明在数组中找到两个数之和为target
            int* ret = malloc(sizeof(int) * 2);//开辟空间
            //保存这两个函数的下标
            ret[0] = it->val;
            ret[1] = i;
            
            *returnSize = 2;//这是返回数组的元素个数
            return ret;//返回开辟数组
        }
        //如果在已经存在的hashTable中没有这个节点,那就把这个节点加入到hashTable中
        insert(nums[i], i);
    }

    
    *returnSize = 0;
    return NULL;
}

5.总结:

以上代码是官方提供的算法。本题花费了很多时间,如果用暴力运算是非常简单的,但是时间复杂度是O(n^2)。为了优化时间复杂度,是花费了很多时间的,最终都还是参考了官方算法才明白怎样优化。如果要想彻底弄清楚,必须要精通数据结构的hash算法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值