leetcode 算法类第一题:两数之和

给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/two-sum
一、代码。代码采用C++编写,共三种解法。
1)暴力法

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

用时1040ms,内存消耗 7.2MB该算法时间复杂度:O(n2) 空间复杂度O(1)
2)两遍哈希

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        //一遍哈希表方法
        //将数组内数据存储到哈希表,value作为键,下标作为值
        unordered_map<int, int> nmap;
        for(int i=0;i<nums.size();++i)
            nmap[nums[i]] = i;
        //对每一个数组内的值查找有无对应的target-value
        for(int i=0;i<nums.size();++i){
            //存在对应的值且不为原值,则返回两个下标
            if(nmap.find(target-nums[i])!=nmap.end() && nmap[target-nums[i]]!=i)
            return {i, nmap[target-nums[i]]};
        }
        return {};
    }
};

用时12ms,内存消耗8.4MB
该算法时间复杂度为O(n),空间复杂度为O(n)
3)一遍哈希

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        //一遍哈希
        unordered_map<int, int> nmap;
        for(int i=0;i<nums.size();++i){
            //向前搜索,每次只搜索该元素之前的元素
            if(nmap.find(target-nums[i]) != nmap.end()) //元素不会重复
                return {nmap[target-nums[i]], i};
            nmap[nums[i]] = i;
        }
        return {};
    }
};

用时8ms, 内存消耗8.3MB
该算法时间复杂度为O(n),空间复杂度为O(n)
二、附属知识:
1)时间复杂度和空间复杂度
对算法的评价可采用时间复杂度和空间复杂度两项进行评估。
时间复杂度为一个算法中所有语句的频度之和,通常只需要标出所属量级。从算法中选取一种对于所研究的问题来说最基本的操作(通常存在于嵌套层数最多的最内层循环体中),并以该基本操作执行的次数作为算法的时间度量。
空间复杂度为算法对输入数据进行运算所需的辅助工作单元和存储计算所需信息的辅助空间,是一种额外空间。
2)vector
容器的评价指标可根据其性能,容器的使用要遵循“4+1”:增删改查+初始化
算法2)和3)返回一个初始化列表是基于C++11中存在vector的构造函数:

vector (initializer_list<value_type> il, const allocator_type& alloc = allocator_type());

返回一个初始化列表时,系统会调用该构造函数创建一个vector类型的临时变量
3)哈希表
哈希表是一种可将节点的位置和节点建立特定关系的数据结构,具体方法为通过哈希函数根据节点的键值确定节点的存储位置。
哈希表采用链地址法避免冲突,每个节点后的链表存储的数据为根据哈希函数得到的相同地址的数据。
算法中采用了find函数,在这里我产生的疑问是哈希表的find函数的时间复杂度为什么是O(1)?如何实现的?
find函数的作用是查找某一个键值是否存在,而哈希函数是根据键值计算节点位置,那查找键值是否还需要迭代呢?
find函数的定义如下:

iterator find(const key_type& key)
{
  size_type n = bkt_num_key(key);
  node* first;
  for ( first = buckets[n];
        first && !equals(get_key(first->val), key);
        first = first->next)
    {}
  return iterator(first, this);
}

哈希表的数据结构部分由vector来实现。find函数首先根据输入的键值计算节点位于哪个bucket,哈希函数可保证计算出的值n不会越界(例如取余%等)。然后在该bucket中根据key值继续查找节点,若该节点不存在则first=NULL,返回iterator(NULL, this)。而哈希表end()函数的实现为:

iterator end() { return iterator(0, this); }

因此可通过比较find的返回值是否为end()查看哈希表中是否存在该值。
由于迭代只会在同一个bucket(即哈希地址产生冲突的节点)中进行,因此在数据量较少的情况下哈希表的find()函数时间复杂度近似为O(1)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值