题目和链接:
理论知识:
242.有效的字母异位词
做题状态:
以前做过,用哈希做的,忘了排序这个事儿,儿豁
题解:
1.哈希
先把s的存进table,再遍历t的,找到就减1,小于0就返回false
class Solution {
public:
bool isAnagram(string s, string t) {
vector<int>table(26,0);
if(s.length()!=t.length())return false;
for(auto ch:s){
table[ch-'a']++;
}
for(auto ch:t){
table[ch-'a']--;
if(table[ch-'a']<0)return false;
}
return true;
}
};
2.哈希+排序
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;
}
};
总结:
既要注重算法归纳,也不能忘记数学本质
349.两个数组的交集
做题状态:
一看就会,一写就废,
题解:
1.哈希
把nums1转为num1这个unordered_set,实现降重,方便查找
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
unordered_set<int>res;
unordered_set<int>num1(nums1.begin(),nums1.end());
for(auto&i:nums2){
if(num1.find(i)!=num1.end()){
res.insert(i);
}
}
return vector<int>(res.begin(),res.end());
}
};
2.也是哈希,嘻嘻
作者:红鼻子Jacplusthon
这种就是没有转成set再转回来,一中有的,如果二中也有就直接放入res,并删除一个重复的,写在这是为了熟悉语法
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
vector<int> res;
unordered_set<int> num_set(nums1.begin(),nums1.end());
for(auto num: nums2) {
if(num_set.find(num)!=num_set.end()) {
res.push_back(num);
num_set.erase(num);
}
}
return res;
}
};
总结:
语法上有些不足
1.vector的push_back,unordered_set的erase
2.set的find
3.两种容器的复制传递,如
vector<int>(res.begin(),res.end());
unordered_set<int> num_set(nums1.begin(),nums1.end());
202.快乐数
题意:
给一个数字,各位数字平方和后产生令一个数字,如果最终为1,输出true,循环下去则输出false
做题状态:
还蛮简单的,用的类似官方题解的第一种方法,题解的2、3泰裤辣
题解:
1.用哈希集合检测循环
挺常规的,不解释了
class Solution {
public:
unordered_set<int>table;
bool isHappy(int n) {
int sum=0;
while(n){
int x=n%10;
n/=10;
x=x*x;
sum+=x;
}
if(sum==1)return true;
if(table.find(sum)!=table.end()){
return false;
}
table.insert(sum);
return isHappy(sum);
}
};
2.快慢指针法
两个指针一快一慢,如果循环总会相遇,否则出现1
下面官方c#code:
class Solution {
public int getNext(int n) {
int totalSum = 0;
while (n > 0) {
int d = n % 10;
n = n / 10;
totalSum += d * d;
}
return totalSum;
}
public bool IsHappy(int n) {
int slowRunner = n;
int fastRunner = getNext(n);
while (fastRunner != 1 && slowRunner != fastRunner) {
slowRunner = getNext(slowRunner);
fastRunner = getNext(getNext(fastRunner));
}
return fastRunner == 1;
}
}
3.数学法
太妙了,数学真帅
所有其他数字都在进入这个4→16→37→58→89→145→42→20→4循环的链上,或者在进入 1 的链上。官方c#code
class Solution {
public int getNext(int n) {
int totalSum = 0;
while (n > 0) {
int d = n % 10;
n = n / 10;
totalSum += d * d;
}
return totalSum;
}
public bool IsHappy(int n) {
HashSet<int> cycleMembers =
new HashSet<int>(new int[8] {4, 16, 37, 58, 89, 145, 42, 20});
while (n != 1 && !cycleMembers.Contains(n)) {
n = getNext(n);
}
return n == 1;
}
}
总结:
二刷可以留意一下后两种方法
1.两数之和
做题状态:
做过,有点简单,跳过了
454.四数相加||
做题状态:
有点简单,一遍过
383.赎金信
做题状态:
有点简单,一遍过
15.三数之和
题意:
给一个数组,其中有三个数满足和为零且下标不同,输出所有这三元组
做题状态:
以前做过,双指针,大体能写出来,细节上debug了一会儿,还是通过了,哈希挺复杂的,不想看了
题解:
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
sort(nums.begin(), nums.end());
int l = 0, r = nums.size() - 1;
vector<vector<int> > ans;
for (int i = 0; i < nums.size() - 2; ++i) {
if (i > 0 && nums[i] == nums[i - 1]) continue; // 跳过重复的元素
int l = i + 1, r = nums.size() - 1;
while (l < r) {
int sum = nums[i] + nums[l] + nums[r];
if (sum == 0) {
ans.push_back({nums[i], nums[l], nums[r]});
while (l < r && nums[l] == nums[l + 1]) ++l; // 跳过重复的元素
while (l < r && nums[r] == nums[r - 1]) --r; // 跳过重复的元素
++l; --r;
} else if (sum < 0) {
++l;
} else {
--r;
}
}
}
return ans;
}
};
总结:
双指针一般在要返回数值而不是下标的题中使用
18.四数之和
题意:
数组中四个不同的数的和为target,返回这样的所有的四元组
做题状态:
跟三数之和挺相似的,几次提交都没有通过,没有发现不同。
题解:
(from代码随想录)
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
vector<vector<int>> result;
sort(nums.begin(), nums.end());
for (int k = 0; k < nums.size(); k++) {
// 剪枝处理
if (nums[k] > target && nums[k] >= 0) {
break; // 这里使用break,统一通过最后的return返回
}
// 对nums[k]去重
if (k > 0 && nums[k] == nums[k - 1]) {
continue;
}
for (int i = k + 1; i < nums.size(); i++) {
// 2级剪枝处理
if (nums[k] + nums[i] > target && nums[k] + nums[i] >= 0) {
break;
}
// 对nums[i]去重
if (i > k + 1 && nums[i] == nums[i - 1]) {
continue;
}
int left = i + 1;
int right = nums.size() - 1;
while (right > left) {
// nums[k] + nums[i] + nums[left] + nums[right] > target 会溢出
if ((long) nums[k] + nums[i] + nums[left] + nums[right] > target) {
right--;
// nums[k] + nums[i] + nums[left] + nums[right] < target 会溢出
} else if ((long) nums[k] + nums[i] + nums[left] + nums[right] < target) {
left++;
} else {
result.push_back(vector<int>{nums[k], nums[i], nums[left], nums[right]});
// 对nums[left]和nums[right]去重
while (right > left && nums[right] == nums[right - 1]) right--;
while (right > left && nums[left] == nums[left + 1]) left++;
// 找到答案时,双指针同时收缩
right--;
left++;
}
}
}
}
return result;
}
};
总结:
1. 不要判断nums[k] > target
就返回了,三数之和 可以通过 nums[i] > 0
就返回了,因为 0 已经是确定的数了,四数之和这道题目 target是任意值。比如:数组是[-4, -3, -2, -1]
,target
是-10
,不能因为-4 > -10
而跳过。但是我们依旧可以去做剪枝,逻辑变成nums[i] > target && (nums[i] >=0 || target >= 0)
就可以了。(from代码随想录)
2.四个数字相加会溢出,int不够……
总结回顾
今天8道题,大部分都做过,所以挺轻松的,没有昨天学链表那么晚了,学到晚上11点o(╥﹏╥)o
map比较万能,但是什么时候用数组,什么时候用set也挺有讲究
三数之和和四数之和两道题:用指针来做,因为哈希不好去重,五六七八数也应该用指针来做。
1.有几道题可以用数组也能用map,但使用map的空间消耗要比数组大一些,因为map要维护红黑树或者符号表,而且还要做哈希函数的运算。所以数组更加简单直接有效。
2.如果数组空间够大,但哈希值比较少、特别分散、跨度非常大,使用数组就造成空间的极大浪费。所以此时一样的做映射的话,就可以使用set了。
ヾ(◍°∇°◍)ノ゙ (*^▽^*)