有效的字母异位词
242题:给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。
示例 1: 输入: s = “anagram”, t = “nagaram” 输出: true
示例 2: 输入: s = “rat”, t = “car” 输出: false
说明: 你可以假设字符串只包含小写字母
数组就是一个简单的哈希表
bool Solution1::isAnagram(string s, string t) {
//将第一个字符串存放到哈希表中,再从哈希表中减去第二个字符串,最后所有元素频次结果为0才表明相同,
//如果小于0则表明两字符串不相同?或先对字符串排序再比较?
if (s.length() != t.length())
{
return false;
}
unordered_map<int, int> map1;
//vector<int> map1(26,0);定义一个初始化为0的26位容器
for (int i = 0; i < s.size(); i++)
{
map1[s[i] - 'a']++;//将字符转换为数字作为索引,其值为出现频次
}
for (int j = 0; j < s.size(); j++)
{
map1[t[j] - 'a']--;
if (map1[t[j] - 'a'] < 0)
{
return false;
}
}
return true;
}
//先排序后比较,但c++编译器会对s==t报错
class Solution {
public:
bool isAnagram(string s, string t) {
if (s.length() != t.length()) {
return false;
}
sort(s.begin(), s.end());
sort(t.begin(), t.end());
return s == t;
}
};
题目383:给你两个字符串:ransomNote 和 magazine ,判断 ransomNote 能不能由 magazine 里面的字符构成。
如果可以,返回 true ;否则返回 false 。
magazine 中的每个字符只能在 ransomNote 中使用一次。
与上一题一样
bool Solution2::canConstruct(string ransomNote, string magazine) {
//先将magazine存入哈希表,再将ransomNote从哈希表中减去,如果有频次为负则false
if (magazine.size() < ransomNote.size())
{
return false;
}
unordered_map<int, int> hmap;
int i ;
for (i = 0; i < magazine.size(); i++)
{
hmap[magazine[i] - 'a']++;
}
for (i = 0; i < ransomNote.size(); i++)
{
hmap[ransomNote[i] - 'a']--;
if (hmap[ransomNote[i] - 'a'] < 0)
{
return false;
}
}
return true;
}
例题49:给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。
字母异位词 是由重新排列源单词的字母得到的一个新单词,所有源单词中的字母通常恰好只用一次。
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string>& strs) {
unordered_map<string, vector<string>> hmap;//将排序后的字符串作为键,一组字母异位词作为值
for (string& str:strs)//难点在于怎么判断字母异位词:排序后相同?统计字符出现次数相同?
{
string key = str;
//hmap[sort(key.begin(),key.end())]++;错误在于++是计数,现在值是字符串,学习新的存入哈希表方法;
//把字符串容器存入哈希表(将字母异位词存入一个索引下)
sort(key.begin(), key.end());
hmap[key].push_back(str);
}
vector<vector<string>> str;
for (auto it = hmap.begin(); it != hmap.end(); it++)
{
str.push_back(it->second);//在容器尾部插入一个元素
}
return str;
}
};
1.学习了哈希表键值对各自存放的类型,并且不能统一的对哈希表++,只有值是数字时才能++,如果存放的是字符串容器,则需要用push_back()方法将值存放在相应的键中。
2.学习了怎么用同一个键表示不同的值,该题中可以排序也可用字符出现次数计数的方法来表示字母异位符
3.排序调用sort()函数,其有两个参数,分别是开始和结束位
4.用it表示哈希表键对应的元素,其中值个数不止一个
例题438:给定两个字符串 s 和 p,找到 s 中所有 p 的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。
异位词 指由相同字母重排列形成的字符串(包括相同的字符串)。
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
//用两个26位的数值容器载入3位字符,滑动窗口每次删一加一判断两容器是否相等
if(s.size()<p.size())
{
return {};//返回数值空容器
}
vector<int> v1(26, 0);
vector<int> v2(26, 0);
vector<int> res;
for (char& ch : p)
{
v1[ch - 'a']++;//存放p字符串
}
int i = 0;
while(i<=s.size()-p.size())
{
int t = i;
while(i == 0 && t<p.size())
{
v2[s[t] - 'a']++;
t++;
}
if(i!=0)
{
v2[s[t - 1] - 'a']--;
v2[s[t + p.size()-1] - 'a']++;
}
if (v2==v1)
{
res.push_back(i);
}
i++;
}
return res;
}
};
采用滑动窗口原理,每次移动时删去后一个,新增前一个元素,比每次重新存入p个大小更方便
两个数组的交集
例题349:给定两个数组 nums1 和 nums2 ,返回 它们的交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
vector<int> res;
if (nums1.size() >= nums2.size())
{
for (int i = 0; i < nums1.size(); i++)
{
auto it=find(res.begin(),res.end(),nums1[i]);//用迭代器it表示较长容器的元素
for (int j = 0; j < nums2.size(); j++)
{
if (nums1[i] == nums2[j] && it==res.end())//find函数返回的地址,end不参与搜索,如果没找到返回.end(),借此判读是否已经存过该数
{
res.push_back(nums1[i]);
}
}
}
}
if (nums1.size() < nums2.size())
{
for (int i = 0; i < nums2.size(); i++)
{
auto it=find(res.begin(),res.end(),nums2[i]);
for (int j = 0; j < nums1.size(); j++)
{
if (nums2[i] == nums1[j] && it==res.end())
{
res.push_back(nums2[i]);
}
}
}
}
return res;
}
};
该方法在leetcode上过了,但在VS编辑器上迭代器it不相容,借鉴后两种方法的find写法
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());//直接将nums1存入nums_set
for (int num : nums2) {
// 发现nums2的元素 在nums_set里又出现过
if (nums_set.find(num) != nums_set.end()) {
result_set.insert(num);//insert方法将num存入set中,会自动去重
}
}
return vector<int>(result_set.begin(), result_set.end());
}
};
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());
}
};
例题350:给你两个整数数组 nums1 和 nums2 ,请你以数组形式返回两数组的交集。返回结果中每个元素出现的次数,应与元素在两个数组中都出现的次数一致(如果出现次数不一致,则考虑取较小值)。可以不考虑输出结果的顺序。
class Solution {
public:
vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
vector<int> num1(1005, 0);
vector<int> num2(1005, 0);
vector<int> res;
for (int a : nums1)
{
num1[a]++;//存数值出现次数
}
for (int b : nums2)
{
num2[b]++;
}
for (int i = 0; i < 1005; i++)
{
int count=0;
while(num1[i] != 0 && num2[i] != 0 && count<min(num1[i],num2[i]))
{
res.push_back(i);//存入的应该是下标,而不是容器里的次数
count++;
}
}
return res;
}
};
键盘输入数值[0,1000]字符方法
vector<int> nums2;
int t;
cout << "please input nums1:" << endl;
while (cin>>t)
{
if (t < 0)//键盘输入数值容器,以负数结束
{
break;
}
nums1.push_back(t);
}
快乐数
例题202:编写一个算法来判断一个数 n 是不是快乐数。
「快乐数」 定义为:
对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。
如果这个过程 结果为 1,那么这个数就是快乐数。
如果 n 是 快乐数 就返回 true ;不是,则返回 false 。
无限循环表明如果不是快乐数,和sum会重复。因此,判断一个数是否在某个集合中,应考虑哈希法
可以用unordered_set存放sum,用insert()函数添加;而vector用push_back向后添加元素
另一个难点:怎么求sum各个位上数字的和
- 为什么把求和函数写入isHappy函数中会超时,而单独提出来就不会超时?
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) {
//无限循环的意思就是sum会重复,把每次的sum存入哈希表中,如果重复就返回false,如果sum为1就返回true
int sum=0;
//因为sum的值没有对应下标的关系,如果按数值做下标则空间占用大,可以用set集合
unordered_set<int> s;
//不确定n的具体位数,怎么分解n,每次sum的位数不同怎么计算下一个sum?
while (1)
{
//求sum:有点技术
if (sum == 1)
{
return true;
}
sum = getSum(n);
if (s.find(sum) != s.end())//在s中找到了重复的sum
{
return false;
}
s.insert(sum);
n = sum;//更新n的值
}
}
};
两数之和
例题1:给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
vector<int> res;
int i, j,t;
for (i = 0; i < nums.size(); i++)
{
t = target - nums[i];
for (j = i+1; j < nums.size(); j++)
{
if (nums[j] == t)
{
res.push_back(i);
res.push_back(j);
return res;
}
}
}
return {};
}
};
时间复杂度o(n^2) ,进阶(提出时间复杂度<O(n*n)的算法)
代码随想录解法:将数组存入无序哈希表中,哈希表中用来存已经访问过的对象。key为值,value为下标。遍历访问数组,在哈希表中查找是否有对应的剩余部分,如果有则返回两者的下标,否则将该数加入哈希表,时间复杂度为O(n)
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 {};
}
};