242.有效字母异位词
思路:
一知半解,看了视频才通透,原来可以通过只需要将 s[i] - ‘a’ 所在的元素做+1 操作,本来不知道怎么和数组联系起来,数组索引做减法字符串直接算ASCII码,主要学会采用字符串的遍历方式
代码:
自己看完解答手搓的代码,一看就会一写就废,通过求和的话只要字符串长度相同都会输出true,那还判断啥...
//傻逼了,用求和判定
class Solution {
public:
bool isAnagram(string s, string t) {
int hash[26] = {0};
int sum = 0;
for(int i = 0; i < s.size(); i++){
hash[s[i]-'a']++;
}
for(int j = 0; j < t.size(); j++){
hash[t[j]-'a']--;
}
for(int k = 0; k < 26; k++){
sum += hash[k];
}
if (sum == 0) return true;
return false;
}
};
正解:
class Solution {
public:
bool isAnagram(string s, string t) {
int record[26] = {0};
for (int i = 0; i < s.size(); i++) {
// 并不需要记住字符a的ASCII,只要求出一个相对数值就可以了
record[s[i] - 'a']++;
}
for (int i = 0; i < t.size(); i++) {
record[t[i] - 'a']--;
}
for (int i = 0; i < 26; i++) {
if (record[i] != 0) {
// record数组如果有的元素不为零0,说明字符串s和t 一定是谁多了字符或者谁少了字符。
return false;
}
}
// record数组所有元素都为零0,说明字符串s和t是字母异位词
return true;
}
};
- 时间复杂度: O(n)
- 空间复杂度: O(1)
349.两个数组的交集
思路:
交集记得要去重,set就可以直接去重,数组的话需要判定新的数再添加。数组对比于set,如{0,5,1000}如果用数组的话会浪费很多空间,因为要定义一个array[1000]。
将nums1存在哈希表内,遍历每一个值,再再nums2中用哈希表查找。要注意返回的时候写法。
代码:
用set
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
unordered_set<int> result_set;//用来存放最后结果,不需要去重
unordered_set<int> num_set(nums1.begin(), nums1.end());//通过迭代器初始化集合元素范围
for (int num : nums2){//遍历每个元素
//利用哈希表查询
if(num_set.find(num) != num_set.end())//当找不到时候会返回尾部迭代器,不等于意思是找到了
result_set.insert(num);
}
//return result_set; 这样是错的,不能直接返回set
return vector<int>(result_set.begin(), result_set.end());//返回开始到结尾位置的元素
}
};
- 时间复杂度: O(mn)
- 空间复杂度: O(n)
直接使用set 不仅占用空间比数组大,而且速度要比数组慢,set把数值映射到key上都要做hash计算的。不要小瞧 这个耗时,在数据量大的情况,差距是很明显的。
用数组:
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
unordered_set<int> result_set; // 存放结果,之所以用set是为了给结果集去重
int hash[1005] = {0}; // 默认数值为0
for (int num : nums1) { // nums1中出现的字母在hash数组中做记录
hash[num] = 1;
}
for (int num : nums2) { // nums2中出现话,result记录
if (hash[num] == 1) {
result_set.insert(num);
}
}
return vector<int>(result_set.begin(), result_set.end());
}
};
- 时间复杂度: O(m + n)
- 空间复杂度: O(n)
202.快乐数
思路:
其中查询是否出现过可以用哈希表,此外要注意判定,先判定是否出现1,再判定是否循环。
代码:
class Solution {
public:
// 取数值各个位上的单数之和
int getSum(int n) {
int sum = 0;
while (n) {
sum += (n % 10) * (n % 10);
n /= 10;
}
return sum;
}
bool isHappy(int n) {
unordered_set<int> set;
while(1) {
int sum = getSum(n);
if (sum == 1) {
return true;
}
// 如果这个sum曾经出现过,说明已经陷入了无限循环了,立刻return false
if (set.find(sum) != set.end()) {
return false;
} else {
set.insert(sum);
}
n = sum;
}
}
};
- 时间复杂度: O(logn)
- 空间复杂度: O(logn)
1.两数之和
思路:
本题,我们不仅要知道元素有没有遍历过,还要知道这个元素对应的下标,需要使用 key value结构来存放,key来存元素,value来存下标,那么使用map正合适
- 数组的大小是受限制的,而且如果元素很少,而哈希值太大会造成内存空间的浪费。
- set是一个集合,里面放的元素只能是一个key,而两数之和这道题目,不仅要判断y是否存在而且还要记录y的下标位置,因为要返回x 和 y的下标。所以set 也不能
此时就要选择另一种数据结构:map ,map是一种key value的存储结构,可以用key保存数值,用value再保存数值所在的下标。
std::unordered_map 底层实现为哈希表,std::map 和std::multimap 的底层实现是红黑树,这道题目中并不需要key有序,选择std::unordered_map 效率更高!
map目的用来存放我们访问过的元素,因为遍历数组的时候,需要记录我们之前遍历过哪些元素和对应的下标,这样才能找到与当前元素相匹配的(也就是相加等于target)
接下来是map中key和value分别表示什么。
这道题 我们需要 给出一个元素,判断这个元素是否出现过,如果出现过,返回这个元素的下标。
那么判断元素是否出现,这个元素就要作为key,所以数组中的元素作为key,有key对应的就是value,value用来存下标。
所以 map中的存储结构为 {key:数据元素,value:数组元素对应的下标}。
在遍历数组的时候,只需要向map去查询是否有和目前遍历元素匹配的数值,如果有,就找到的匹配对,如果没有,就把目前遍历的元素放进map中,因为map存放的就是我们访问过的元素。
代码:
手搓:
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
std::unordered_map <int,int> map;
for (int i = 0; i < nums.size(); i++){
int num = target - nums[i];
if(map.find(num) != map.end()){
return {map.find(num)->second, i};
}
map.insert(pair<int,int>(nums[i],i));
}
return {};
}
};
//卡哥标准代码
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
std::unordered_map <int,int> map;
for(int i = 0; i < nums.size(); i++) {
// 遍历当前元素,并在map中寻找是否有匹配的key
auto iter = map.find(target - nums[i]);
if(iter != map.end()) {
return {iter->second, i};
}
// 如果没找到匹配对,就把访问过的元素和下标加入到map中
map.insert(pair<int, int>(nums[i], i));
}
return {};
}
};
- 时间复杂度: O(n)
- 空间复杂度: O(n)