如果你时间比较紧迫,为了找工作而刷题,我建议你先刷热门推荐,一共100道题。
先刷热题 HOT 100,再刷精选 TOP 面试题,之后刷其他的题。
LeetCode 1 两数之和:暴力枚举+哈希表
给定一个整数数组nums和一个目标值target,请你在该数组中找出和为目标值的那两个整数,并返回他们的数组下标。 你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。
示例:
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9 所以返回 [0, 1]
1.暴力法
思路及算法
最容易想到的方法是枚举数组中的每一个数 x,寻找数组中是否存在 target - x。当我们使用遍历整个数组的方式寻找 target - x 时,需要注意到每一个位于 x 之前的元素都已经和 x 匹配过,因此不需要再进行匹配。而每一个元素不能被使用两次,所以我们只需要在 x 后面的元素中寻找 target - x。
暴力法很简单,遍历每个元素 x,并查找是否存在一个值与 target - x 相等的目标元素。
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 {};
}
}; //时间复杂度:O(N^2) 空间复杂度:O(1)O(1)
2.(利用哈希表)
思路及算法
能够快速寻找数组中是否存在目标元素。如果存在,我们需要找出它的索引。使用哈希表,可以将寻找 target - x 的时间复杂度降低到从 O(N)降低到 O(1)。这样我们创建一个哈希表,对于每一个x,我们首先查询哈希表中是否存在 target - x,然后将 x 插入到哈希表中,即可保证不会让 x 和自己匹配。
事实证明,我们可以一次完成。在进行迭代并将元素插入到表中的同时,我们还会回过头来检查表中是否已经存在当前元素所对应的目标元素。如果它存在,那我们已经找到了对应解,并立即将其返回。
class Solution
{
public int[] twoSum(int[] nums, int target)
{
HashMap<Integer,Integer> map=new HashMap(); //创建哈希表
for(int i=0;i<nums.length;i++)
{
if(map.containsKey(target-nums[i]))//找到结果 直接返回
return new int[]{map.get(target-nums[i]),i};
else//找不到结果 将当前值存入哈希
map.put(nums[i],i);
}
return new int[]{-1,-1};
}
}
暴力枚举实现
#include<iostream>
#include<vector>
using namespace std;
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 {};
}
};
int main()
{
vector<int> q(4); //创建一个四位数组
q.emplace_back(2); //依次追加
q.emplace_back(7);
q.emplace_back(11);
q.emplace_back(15);
int target = 9;
Solution s; //实例化对象s
vector<int> res= s.twosum(q, target);//(nums[i] + nums[j] == target) ?
for (auto i : res) {
cout << i << endl;
}
}
哈希表法
利用哈希表的特性(查找快速),在遍历数组的同时,检查每个元素达成目标值所需要的另一元素是否存在于哈希表中,从而实现在遍历一遍数组的前提下,就能够找出符合要求的两个数组元素。
复杂度分析
时间复杂度:遍历数组 O ( n ) ,对每个元素需要以 O ( 1 ) 的代价来判断是否存在另一个元素
空间复杂度:O ( n ) ,主要是用来开辟哈希表空间
拓展
c++11 之emplace_back 与 push_back的区别
c++开发中我们会经常用到插入操作对stl的各种容器进行操作,比如vector,map,set等。在引入右值引用,转移构造函数,转移复制运算符之前,通常使用push_back()向容器中加入一个右值元素(临时对象)时,首先会调用构造函数构造这个临时对象,然后需要调用拷贝构造函数将这个临时对象放入容器中。原来的临时变量释放。这样造成的问题就是临时变量申请资源的浪费。
引入了右值引用,转移构造函数后,push_back()右值时就会调用构造函数和转移构造函数,如果可以在插入的时候直接构造,就只需要构造一次即可。这就是c++11 新加的emplace_back。
emplace_back 不用调用拷贝构造函数
template <class... Args>
void emplace_back (Args&&... args);
在容器尾部添加一个元素,这个元素原地构造,不需要触发拷贝构造和转移构造。而且调用形式更加简洁,直接根据参数初始化临时对象的成员。
vector<int>
一、什么是vector? 向量(Vector)是一个封装了动态大小数组的顺序容器(Sequence Container)。跟任意其它类型容器一样,它能够存放各种类型的对象。可以简单的认为,向量是一个能够存放任意类型的动态数组。
1 基本操作
(1)头文件 #include<vector>;
(2)创建vector对象,vector vec;
首元素 vec.begin(); 末元素 vec.end();
(3)尾部插入数字:vec.push_back(a); 常用vec.emplace_back 不需要拷贝构造函数;
(4)使用下标访问元素,cout<<vec[0]<<endl; //记住下标是从0开始的。
(5)使用迭代器访问元素.
vector::iterator it;
for(it=vec.begin();it!=vec.end();it++)
cout<<*it<<endl;
(6)插入元素: vec.insert(vec.begin()+i,a); //在第i个元素后面插入a;
(7)删除元素: vec.erase(vec.begin()+2); //删除第3个元素
vec.erase(vec.begin()+i,vec.end()+j); //删除区间[i,j-1];区间从0开始
(8)向量大小:vec.size();
(9)清空:vec.clear(); //清空之后,vec.size()为0
unordered_map的使用
- 头文件:
#include<unordered_map>
2、定义一个哈希表(我们以Key和Value都是int变量为例):
unordered_map<int,int> Hash;
3、哈希表的建立有下面几种方法:
Hash[1]=3;
Hash.insert<make_pair(1,3)>;
Hash.insert({ {1,3},{2,4} });
4、迭代器:
unordered_map<int,int>::iterator it;
5、利用迭代器访问变量:
it->first;
it->second;
6、哈希表的查找:
it=Hash.find(1);
若找不到,返回的是Hash.end();
7、修改哈希表:
Hash[1] = 4;
8、清除哈希表:
Hash.erase(1);
Hash.clear();