前言
LeetCode题目:LeetCode 242、349、202、1
Takeaway:哈希表知识;STL容器:set 和 map的使用方法;不同容器的不同版本以及其底层逻辑。
一、LeetCode 242
这题是用数组来实现哈希表,选择数组的原因是哈希表内元素很少,顺便也考验一点点对ASCII码的理解。
class Solution {
public:
bool isAnagram(string s, string t) {
int hash_nums[26] = {0};
if(s.size() != t.size()){
return false;
}
for(int i=0; i<s.size(); i++){
hash_nums[s[i]-'a']++;
// cout<<s[i]<<":"<<hash_nums[s[i]-'a']<<endl;
}
// cout<<"_______"<<endl;
for(int i=0; i<t.size(); i++){
hash_nums[t[i]-'a']--;
// cout<<t[i]<<":"<<hash_nums[t[i]-'a']<<endl;
}
int flag = 1;
for(int i=0; i<26; i++){
if(hash_nums[i] != 0){
flag = 0;
break;
}
}
if(flag == 1){
return true;
}
return false;
}
};
二、LeetCode 349
本题就需要使用set来实现哈希表了,unordered_set不含重复元素满足题目要求,并且速度比普通set快。所以选用它。这题也学到了set的用法和vector与set的转换方法。
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
unordered_set<int> set1;
for(int i=0; i<nums1.size(); i++){
set1.insert(nums1[i]);
}
unordered_set<int> set2;
for(int i=0; i<nums2.size(); i++){
if(set1.find(nums2[i]) != set1.end()){
set2.insert(nums2[i]);
}
}
return vector<int>(set2.begin(), set2.end());
}
};
三、LeetCode 202
本题也是使用set来实现哈希表的一道题,如果出现无限循环,那么说明当前的n之前一定已经出现过了,所以我们就可以用哈希表来快速检查这个数是否出现过。此题难度可能在复杂的模拟过程中,但是我做过很多类似的模拟题,所以整体没什么难度。
class Solution {
public:
bool isHappy(int n) {
unordered_set<int> existNum;
while(n != 1){
/* 找到重复元素了 */
if(existNum.find(n) != existNum.end()){
return false;
}
existNum.insert(n);
vector<int> eachNum;
int num;
while(n>0){
num = n%10;
n = n/10;
eachNum.push_back(num*num);
}
for(int i=0; i<eachNum.size(); i++){
n += eachNum[i];
}
}
return true;
}
};
四、LeetCode 1
1、排序+双指针解法
这个解法是我在没看参考文章前自己写的,并没有使用到map容器,并且算法复杂度石O(nlogn),要优于参考文章复杂度为O(n^2)的算法,但是考虑到本题的目的是训练我们如何使用map容器,所以我又再写了一个使用map的解法。
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
int left = 0;
int right = nums.size()-1;
int num1, num2;
//sort会改变原数组,得留个备份
vector<int> nums_orginal = nums;
//排序后才能用双指针操作
sort(nums.begin(), nums.end());
vector<int> ans;
//left == right就到头了
while(left != right){
int cur = nums[left]+nums[right];
if(cur == target){
num1 = nums[left];
num2 = nums[right];
break;
}else if(cur > target){
right--;
}else{
left++;
}
}
//找两个数在原数组的位置,这就是为什么原数组要存个备份
for(int i = 0; i<nums_orginal.size(); i++){
if(nums_orginal[i] == num1){
ans.push_back(i);
// 防止出现[3,3]这种俩数字一样的情况
nums_orginal[i] = -1;
break;
}
}
for(int i = 0; i<nums_orginal.size(); i++){
if(nums_orginal[i] == num2){
ans.push_back(i);
break;
}
}
return ans;
}
};
2、使用map实现
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int, int> map;
vector<int> ans;
for(int i=0; i<nums.size(); i++){
// 在map中寻找值为target-nums[i]的元素,满足条件就说明找到了
if(map.find(target-nums[i])!=map.end()){
// ans里存储的是位置
ans.push_back(i);
ans.push_back(map[target-nums[i]]);
}
map.insert(pair<int, int>(nums[i],i));
}
return ans;
}
};
总结
今天题目都是要靠哈希表来解决的,哈希表的实现方法主要有数组,set 和 map 这三种,每种都有不同的使用场景:
当哈希表内元素很少的时候,使用数组会方便一些;
当哈希表内元素很多的时候(或者是未知大小,此时你无法定义数组),使用 set 会更好一些;
当哈希表需要键值对的时候,比如 Leetcode 1 这道题,需要同时保存元素的值和位置,这就是一个键值对,此时使用 map 是更好的解法。
此外,对于 set 和 map,每个都有标准版,multi版和unorder版,具体使用哪个要根据实际需求来,其中标准版和multi版都是使用红黑树来实现的,其特点是有序但效率慢,其中multi可以重复;而unordered版则是无顺序的,但是其优点速度快,其底层实现是哈希表。