代码随想录
C++中set和map的使用
用两个数组来分别统计两个字符串中的字符出现的次数,然后再比较两个数组是否相等即可,若相等则返回true,否则返回false。
class Solution {
public:
bool isAnagram(string s, string t) {
int cnt_s[26] = {0};
int cnt_t[26] = {0};
int size_s = s.size();
for(int i=0;i<size_s;i++)
cnt_s[s[i]-'a']++;
int size_t = t.size();
for(int i=0;i<size_t;i++)
cnt_t[t[i]-'a']++;
//比较两个数组
for(int i=0;i<26;i++)
if(cnt_s[i] != cnt_t[i]) return false;
return true;
}
};
实现思路是一致的,更可以只使用一个数组来统计字符出现的次数,数组初始化全0,遍历第一个字符串用加操作,遍历第二个数组时用减操作,然后判断数组是否全0,若是则返回true,否则返回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;
}
};
这个题的字符串只包含小写字母,若还有其他字符则会导致字符的ASCII码不连续或者跨度较大,数组的大小也应该做相应的变化,当然也可以使用map来对字符计数。
用unordered_set来求解。先遍历一个数组,将数组的数据添加到集合中(同时会去重),然后再遍历另一个数组,每个数在集合中查找,若能查找到,这个数就是公共的数。遍历完成之后还需要对结果去重,因为第二个数组中可能会存在重复的数据。代码如下:
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
unordered_set<int> s;
vector<int> vec;
int size1 = nums1.size();
for(int i=0;i<size1;i++)
s.insert(nums1[i]);
int size2 = nums2.size();
for(int i=0;i<size2;i++)
{
auto pos = s.find(nums2[i]);
if(pos != s.end())
vec.push_back(nums2[i]);
}
//对结果去重
s.clear();
for(int i=0;i<vec.size();i++)
s.insert(vec[i]);
vec.clear();
for(auto it = s.begin();it != s.end();it++)
vec.push_back(*it);
return vec;
}
};
代码随想录给出的代码如下:
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
unordered_set<int> result_set; // 存放结果,之所以用set是为了给结果集去重
unordered_set<int> nums_set(nums1.begin(), nums1.end());
for (int num : nums2) {
// 发现nums2的元素 在nums_set里又出现过
if (nums_set.find(num) != nums_set.end()) {
result_set.insert(num);
}
}
return vector<int>(result_set.begin(), result_set.end());
}
};
这个题限制了数据的大小:0 <= nums1[i], nums2[i] <= 1000,所以也是可以用数组来实现的,只是占用的空间会大一些。代码如下:
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
int cnt[1005] = {0};
unordered_set<int> s;
for(int i:nums1) cnt[i]++;
for(int i:nums2)
if(cnt[i] > 0) s.insert(i);
return vector<int>(s.begin(),s.end());
}
};
此外,也可以只用数组实现,如下:
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
bool flag[1000] = {false};
int size = nums1.size();
int i;
vector<int> result;
for(i = 0; i < size; i++)
flag[nums1[i]] = true;
size = nums2.size();
for(i = 0; i < size; i++)
if(flag[nums2[i]]){
result.push_back(nums2[i]);
flag[nums2[i]] = false;
}
return result;
}
};
这个题看了之后没什么思路,直接看代码随想录的求解思路。
看了思路之后,发现自己没有理解透题目的意思,题目中有一个很关键的信息:无限循环,也就是说如果和sum出现重合这个数就不是快乐数,这一点很重要。判断一个数是否出现过,那就可以使用哈希表了。自己写的代码如下:
class Solution {
public:
bool isHappy(int n) {
unordered_set<int> sum_set;
int sum = n;
while(sum != 1)
{
int data = sum;
sum = 0;
//求平方和
do
{
int tmp = data % 10; //得到最低位
data /= 10; //去掉最低位
sum += tmp * tmp;
}while(data);
if(sum_set.find(sum) != sum_set.end()) return false;
sum_set.insert(sum);
}
return true;
}
};
代码随想录给出的代码如下:
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;
}
}
};
这个题的难点在与理解题目的意思,即如果不是快乐数则会无限循环,这意味着会有重复的数据出现,这是本题的关键,判断一个数据是否出现过就用哈希表。
两个数之和num1 + num2 = target,则num1 = target - num2,因此遍历数组,然后查找target-num[i]
是否出现过,查找某一个数是否出现过可以用哈希表。代码实现如下:
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int,int> mp;
vector<int> res;
for(int i=0;i<nums.size();i++)
mp[nums[i]] = i;
for(int i=0;i<nums.size();i++)
{
auto pos = mp.find(target-nums[i]);
if(pos != mp.end() && pos->second != i)
{
res.push_back(i);
res.push_back(pos->second);
break;
}
}
return res;
}
};
在写代码的时候把键和值弄反了,导致结果错误,所以在使用map的时候一定要明确键和值分别是什么,在这个题中,键是数组中的值,值是其对应的下标,原因是最后要得到的是数组的下标,二这个下标是要通过差值来查找的,所以键应该是数组中的数值。另外,找到之后需要判断这个数是不是本身,比如4+4=8,这种情况是要排除的,即pos->second != i
.
代码随想录也是用unordered_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++) {
// 遍历当前元素,并在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 {};
}
};
使用map的时候一定要明确键和值分别是什么,否则就会出错。对顺序没有要求时使用unordered_map,效率更高。这题因为同时要保存数组的下标和值,所以不能使用集合。