💁♂️个人主页:进击的荆棘
👇作者其它专栏:
相关题解
1.两数之和
算法思路:
●若我们可以事先将【数组内的元素】和【下标】绑定在一起存入【哈希表】中,然后直接在哈希表中查找每一个元素的target-nums[i],就能快速的找到【目标和的下标】。
●小技巧:可以不用将元素全部放入到哈希表之后,再来二次遍历(因为要处理元素相同的情况)。而是在将元素放入到哈希表中的【同时】,直接来检查表中是否已经存在当前元素所对应的目标元素(既target-nums[i])。若它存在,就说明已经找到了对应解,并立即将其返回。无需将元素全部放入哈希表中,提高效率。
●因为哈希表中查找元素的时间复杂度为O(1),遍历一遍数组的时间复杂度为O(N),因此可以将时间复杂度降到O(N)。
这是一个典型的【用空间换时间】的方式。
cpp
//法一:暴力枚举
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
for(int i=0;i<nums.size();i++){
for(int j=i-1;j>=0;j--){
if(nums[i]+nums[j]==target){
return {i,j};
}
}
}
return {0,0};
}
};
//法二:用哈希表做优化
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int,int> hash;//<nums[i],i>
for(int i=0;i<nums.size();i++){
int x=target-nums[i];
//类似于暴力,只不过此数之前的数都存在了哈希表中
if(hash.count(x)) return {hash[x],i};
//将此数添加至哈希表
hash[nums[i]]=i;
}
return {-1,-1};
}
};
2.判定是否互为字符重排
算法思路:
1.当两个字符串的长度不相等的时候,是不可能构成互相重排的,直接返回false;
2.若两个字符串能够构成互相重排,那么每个字符串中【各个字符】出现的【次数】一定是相同的。因此,可以分别统计出这两个字符串中各个字符出现的次数,然后逐个比较是否相等即可。这样的话,就可以选择【哈希表】来统计字符串中字符出现的次数。
cpp
class Solution {
public:
bool CheckPermutation(string s1, string s2) {
if(s1.size()!=s2.size()) return false;
int hash[26]={0};
for(auto ch:s1){
hash[ch-'a']++;
}
for(auto ch:s2){
hash[ch-'a']--;
if(hash[ch-'a']<0) return false;
}
return true;
}
};
3.存在重复元素
算法思路:
分析题目,出现【至少两次】的意思就是数组中存在重复的元素,因此可以无需统计元素出现的数目。仅需在遍历数组的过程中,检查当前元素【是否在之前已经出现过】即可。
因此可以利用哈希表,仅需存储【数组内的元素】。在遍历数组的时候,一边检查哈希表中是否已经出现过当前元素,一边将元素加入到哈希表中。
cpp
class Solution {
public:
bool containsDuplicate(vector<int>& nums) {
unordered_set<int> hash;
for(auto& e:nums){
if(hash.count(e)) return true;
else hash.insert(e);
}
return false;
}
};
4.存在重复元素 II
算法思路:
解决该问题需要我们快速定位到两个信息:
●两个相同的元素;
●这两个相同元素的下标。
因此,可以使用【哈希表】,令数组内的元素做key值,该元素所对应的下标做val值,将【数组元素】和【下标】绑定到一起,存入到【哈希表】中。
思考:
若数组内存在大量的【重复元素】,而判断下标所对应的元素是否符合条件的时候,需要将不同下标的元素作比较,该如何处理?
**答:**这里运用了一个【小贪心】。
按照下标【从小到大】的顺序遍历数组,当遇到两个元素相同,并且比较它们的下标时,这两个下标一定是距离最近的,因为:
●若当前判断符合条件直接返回true,无需继续往后查找。
●若不符合条件,那么前一个下标一定不可能与后续相同元素的下标匹配(因为下标在逐渐变大),所以就可以大胆舍去前一个存储的下标,转而将其换成新的下标,继续匹配。
cpp
class Solution {
public:
bool containsNearbyDuplicate(vector<int>& nums, int k) {
unordered_map<int,int> hash;
for(int i=0;i<nums.size();i++){
if(hash.count(nums[i])==1){
//i的下标一定比之前存的下标大
if(i-hash[nums[i]]<=k)
return true;
}
//因为abs(i-j)<=k,则两个相同的值一定是越近,绝对值之差越小,可以放心覆盖之前存的下标
hash[nums[i]]=i;
}
return false;
}
};
5.字母异位词分组
算法思路(哈希表+排序):
互为字母异位词的单词有一个特点:将它们【排序】后,两个单词应是【完全相同】的。
所以,可以利用这个特性,将搭单词按照字典序排序,若排序后的单词相同的话,就划分到同一组中。
这时就要处理两个问题:
●排序后的单词与原单词需要能相互映射;
●将排序后相同的单词,【划分到同一组】;
利用语言提供的【容器】的强大的功能就能实现这两点:
●将排序后的字符串(string)当作哈希表的key值;
●将字母异位词数组(string[])当成val值。
定义一个【哈希表】即可解决问题。
cpp
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string>& strs) {
unordered_map<string,vector<string>> hash;
//1.将字母异位词分组
for(auto& s:strs){
string tmp=s;
//通过排序来确定是否为异位词
sort(tmp.begin(),tmp.end());
hash[tmp].push_back(s);
}
//2.将结果提取到ret中
vector<vector<string>> ret;
//第一种写法
// for(auto& e:hash){
// ret.push_back(e.second);
// }
//第二种写法
for(auto& [x,y]:hash){
ret.push_back(y);
}
return ret;
}
};