哈希表的思路非常好。
class Solution {
public:
bool isAnagram(string s, string t) {
vector<int> hash(26, 0);
for (int i = 0; i < s.size(); i++) {
hash[s[i] - 'a']++;
}
for (int i = 0; i < t.size(); i++) {
hash[t[i] - 'a']--;
}
for (int i = 0; i < hash.size(); i++) {
if (hash[i] != 0) {
return false;
}
}
return true;
}
};
C++中的set容器,需要了解一下源码。后面再了解吧,刚开始只要知道思路就好了,unordered_set是一个库,包括nums.begin(),nums.end()需要查看源码认真总结。其中insert和set对应的东西就够多了,还要多学习。
后边需要常回来看看。
比较难,使用了orderred的
#include
#include
#include
#include
#include
using std::cout;
using std::endl;
using std::vector;
using std::string;
using std::unordered_set;
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
unordered_set<int> resultSet;
unordered_set<int> nums_set(nums1.begin(), nums1.end());
for (int num : nums2) {
if (nums_set.find(num) != nums_set.end()) {
resultSet.insert(num);
}
}
return vector<int>(resultSet.begin(), resultSet.end());
}
};
int main() {
//测试
vector<int> linshi = { 1,2,3,4,5 };
cout << *(linshi.begin())<<endl;
cout << *(linshi.end())<<endl;
cout << (linshi.size())<<endl;
return 0;
}
依然是对于unordered_set的使用,对数字各个位上的求和写法非常漂亮值得借鉴。目前对于unordered_set的find(),end(),insert()的用法更加熟悉了。STL底层库很值得学习。
class Solution {
public:
int getSum(int n) {
int sum = 0;
while (n) {
sum += (n % 10) * (n % 10);
n = n / 10;
}
return sum;
}
bool isHappy(int n) {
unordered_set<int> set;
while (1) {
n = getSum(n);
if (n == 1) {
return true;
}
else {
if (set.find(n) != set.end()) {
return false;
}
else
{
set.insert(n);
}
}
}
}
};
这道题用到map的数据结构,返回空vector的方式第一次见,以后可以学着使用。从代码可以看出,两种vector返回方式都是合理的。第一种更简洁方便。
同时根据题目5,map的key,value索引也可以用简洁的方式,pair应该是map内部的数据结构。
第一种:
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int, int> map;//先不用初始化,为什么呢?因为题目要求数组的同一个元素在答案里不能重复出现,如果先初始化,后边用map.find会造成干扰;另外,map的初始化比较复杂吧
auto length = nums.size();
for (int i = 0; i < length; i++) {
auto index = map.find(target - nums[i]); //map.find()寻找的是key,返回的是(key,value)
if (index != map.end()) {
return { index->second,i };//这种返回空vector列表的方式是第一次见到,->second,选择的是value,->first对应的是key.
}
else
{
map.insert(pair<int, int>(nums[i], i));//pair数据结构是map内部的数据组织结构
//map[nums[i]]=i;
}
}
return {};//这种返回空vector列表的方式是第一次见到
}
};
第二种:
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int, int> map;//先不用初始化,为什么呢?因为题目要求数组的同一个元素在答案里不能重复出现,如果先初始化,后边用map.find会造成干扰;另外,map的初始化比较复杂吧
auto length = nums.size();
for (int i = 0; i < length; i++) {
auto index = map.find(target - nums[i]); //map.find()寻找的是key,返回的是(key,value)
if (index != map.end()) {
return vector<int>{ index->second,i };//这种返回空vector列表的方式是第一次见到,->second,选择的是value,->first对应的是key.
}
else
{
map.insert(pair<int, int>(nums[i], i));//pair数据结构是map内部的数据组织结构
}
}
return vector<int>{};//这种返回空vector列表的方式是第一次见到
}
};
代码随想录的方法确实好。我起初的想法是nums1,nums2先组合成一个map,同理nums3,nums4也组合成一个map.之后遍历一个map,find一个map,但是map好像不能遍历。代码随想录这个答案最节省空间。
同时学到了:map内部元素索引的方法
class Solution {
public:
int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
unordered_map<int, int> map;
for (int num1 : nums1) {
for (int num2 : nums2) {
map[num1 + num2]++; //map的key,value值是可以这样进行赋值的。
}
}
int cout{ 0 };
for (int num3 : nums3) {
for (int num4 : nums4){
if (map.find(0 - num3 - num4) != map.end()) {//find()是在找key,不是value
cout += map[0 - num3 - num4];//map的value是可以这样索引的
}
}
}
return cout;
}
};
代码随想录里的解法更快,好的方面:1.添加了数组长度判断,可以快速判定false的情况。2.数组元素值小于0直接放在第二个循环里,效率更高,我的放在了第三个循环。
给自己一个提醒,按自己的思路写完一道题之后,缓一缓,改进一下代码效率。
我的思路:借鉴第一道题,时间复杂度O(n).
class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
int data[26]={0};
for (int i = 0; i < magazine.length(); i++) {
data[magazine[i]-'a']++;
}
for (int i = 0; i < ransomNote.length(); i++) {
data[ransomNote[i]-'a']--;
}
for (int i = 0; i < 26; i++) {
if(data[i] < 0) {
return false;
}
}
return true;
}
};
代码随想录里的解法:
class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
int record[26] = {0};
//add
if (ransomNote.size() > magazine.size()) {
return false;
}
for (int i = 0; i < magazine.length(); i++) {
// 通过record数据记录 magazine里各个字符出现次数
record[magazine[i]-'a'] ++;
}
for (int j = 0; j < ransomNote.length(); j++) {
// 遍历ransomNote,在record里对应的字符个数做--操作
record[ransomNote[j]-'a']--;
// 如果小于零说明ransomNote里出现的字符,magazine没有
if(record[ransomNote[j]-'a'] < 0) {
return false;
}
}
return true;
}
};
代码随想录的逻辑非常漂亮,值得反复钻研,这是一道重点题,很重要很重要,这是目前遇到最难的一道。
这是双指针法的应用,思路非常漂亮,值得反复钻研。
做题提醒:
按照给出的示例提示去写代码,同时还要关注特例的情况,不注重特例情况就会出现问题。
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) { //三数之和,输入是一个数组,输出是一组三元组
vector<vector<int>> result;//设置一个返回值
sort(nums.begin(), nums.end());//默认升序排序
for (int i = 0; i < nums.size(); i++) { //nums.size()不用跟-1
if (nums[i] > 0) {//这种情况不可能在升序数值中找出和为0的数
return result;//返回至今保留的列表(内部的逻辑也是升序排列)
}
if (i > 0 && nums[i] == nums[i - 1]) {//对第一个数进行去重,i>0是因为i-1无效
continue;
}
int left = i + 1;
int right = nums.size() - 1;
while (left < right) { //不能是left!=right,因为如果left和right紧挨着,left++,right--之后,left>right(这就有问题了)
//left!=right不能使用,还有一层原因就是当输入数组为[0,0,0]时,对第二个数,第三个数去重时会突然出现left>right的情况,这道题还是比较复杂的。还是用left
if (nums[i] + nums[left] + nums[right] > 0) {
right--;
}
else if (nums[i] + nums[left] + nums[right] < 0) {
left++;
}
else {
result.push_back({ nums[i], nums[left], nums[right] });
while (right > left && nums[right] == nums[right - 1]) { //这里的right > left不能少,特例依然是[0,0,0]
right--;
}//对第三个数进行去重
while (right > left && nums[left] == nums[left + 1]) {//这里的right > left不能少,特例依然是[0,0,0]
left++;
}//对第二个数进行去重
left++;
right--;
}
}
}
return result;
}
};
三数之和的扩展,不过其中的特殊情况比较诡异,明天早上解决就ok.
这道题的解法是给三数之和套上一个循环。此时有几个特殊情况:
1.target可以大于等于0,也可以小于0.(当target>=0,排序后只要第一个数>target,就可以跳出循环了;当target<0,第一个数>target不可以跳出循环,这一点非常重要)所以情况就是(nums[i]>=0,且nums>target,才满足跳出条件),这一个思路很重要。
2.代码中使用break的原因是,代码框架为将四数之和,变为一个数和三个数之和。第一个数大于0并且大于target,又因为升序排列,所以必然可以直接result.但是第二层的三个数之和的循环,只是i确定之下的局部问题,遇到第一个数大于0并且大于target的情况,只需要进行局部跳出循环即可,所以用break;
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
vector<vector<int>> result;
sort(nums.begin(), nums.end());//默认升序排列
for (int i = 0; i < nums.size(); i++) {
//还要考虑特殊情况,此处的特殊情况比较复杂了,
if (nums[i] > target && nums[i] >= 0){//将target>=0,<0都包括进去了。
return result;
}
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
//这里还要防止这两个数会不会重复,稍后再想
for (int j = i + 1; j < nums.size(); j++) {
if (nums[j] > target - nums[i] && nums[j] >= 0) { //二重循环里处理的是三数之和,此时的target = target - nums[i]
break;
}
if (j > i + 1 && nums[j] == nums[j - 1])
{
continue;
}
int left = j + 1;
int right = nums.size() - 1;
while (left < right) {
if (static_cast<long>(nums[i]) + nums[j] + nums[left] + nums[right] < target) {
left++;
}
else if (static_cast<long>(nums[i]) + nums[j] + nums[left] + nums[right] > target){
right--;
}
else {
result.push_back({ nums[i],nums[j],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;
}
};