总结
- 有两种哈希结构set,map,其中又分为set,multiset,unordered_set,前两者底层实现是红黑树,因此是有序的,多重表的键值可以重复;而无序集合查询效率更高。
- 常用函数,insert(),push_back(),pop(),begin(),end(),empty();
- 集合的快速初始化,可以快速联动,例如unordered_set<int>nums_in_1(nums1.begin(),nums1.end());
- map的使用,map.insert(pair<int,int>(key,val))
- 集合的遍历:for(auto val : nums1){}
- 统计数字或者字母可以直接使用
- abcd都是int大整数,想要结果正常化可以先加之后再强制转化(long long )a+b+c+d
01字母异位词
Problem: 242. 有效的字母异位词
解题方法
简单题,使用数组就可以做到
Code
#include <unordered_set>
class Solution {
public:
bool isAnagram(string s, string t) {
int charNum[26] = {0};
int i;
for(i = 0;i<s.length();i++){
charNum[s[i]-'a']++;
}
for(i = 0;i<t.length();i++){
charNum[t[i]-'a']--;
}
for(i = 0;i<26;i++){
if(charNum[i]!=0){
return false;
}
}
return true;
}
};
02数组交集
Problem: 349. 两个数组的交集
[TOC]
解题方法
刚开始想到的是用一个集合,用其中一个vector,后来发现vector其中去重搞不定。
复杂度
时间复杂度:
两个向量长度为n,m; 因为每个元素都得遍历,所以复杂度为n+m,查找元素时候,由于是无序表所以复杂度为m
空间复杂度:
n需要申请额外的一个集合的空间
Code
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
unordered_set<int> nums_in_1(nums1.begin(),nums1.end());//数据结构之间的快速联动
unordered_set<int> result;
for (int val : nums2) {
if (nums_in_1.find(val)!=nums_in_1.end()) {
result.insert(val);
}
}
return vector<int>(result.begin(),result.end());
}
};
03快乐数
Problem: 202. 快乐数
[TOC]
思路
想到了通过哈希表检测循环,如果为1则true,如果为其他的则false.通过这题学到了如何遍历一个数的各个数字,while(n){ c=n%10;n=n/10;}
复杂度
时间复杂度:
n
空间复杂度:
添加空间复杂度, 示例:
Code
class Solution {
public:
bool isHappy(int n) {
unordered_set<int> sqare_sum;
sqare_sum.insert(n);
n = calculate_sqare_sum(n);
while(sqare_sum.find(n)== sqare_sum.end()){
sqare_sum.insert(n);
n = calculate_sqare_sum(n);
}
if(n == 1){
return true;
}
else{
return false;
}
}
int calculate_sqare_sum(int n) {
int sum = 0;
while (n) {
sum += (n % 10) * (n % 10);
n=n/10;
}
return sum;
}
};
04两数之和
Problem: 1. 两数之和
[TOC]
思路
看到这一题没有想到用map解题,只想到了复杂的判断循环,导致越写越复杂。 凡是需要用到去重的,都可以使用哈希结构。
解题方法
描述你的解题方法
复杂度
时间复杂度:
哈希表查找效率是logn,此处使用的是无序map所以结果为O(1),每个元素都需要遍历,最后复杂度是n
空间复杂度:
添加空间复杂度, 示例:
Code
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int,int> index_map;
for(int i = 0;i < nums.size();i++){
unordered_map<int, int>::iterator it = index_map.find(target-nums[i]);
if(it!=index_map.end()){
//如果找到了对应的值
return {it->second,i};
}
else{
index_map.insert(pair<int,int>(nums[i],i));//把值和索引存进去
}
}
return {};
}
};
05四数相加等于0
Problem: 454. 四数相加 II
[TOC]
思路
- 刚看到这题想到的是用map记录每个数字出现的次数,然后三个for循环得出0-i-j-k之后在map里查找,但是时间复杂度太高以及unordered_map导致超时。两个for循环把复杂度降到了
- 对于统计数字出现次数的map不够熟悉,记录如下:
unordered_map<int,int> ab_map;
for (int a: nums1){
for(int b: nums2){
ab_map[a+b]++;//先插入a+b默认值为0,之后++实现+1的操作。
}
}
Code
class Solution {
public:
int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3,
vector<int>& nums4) {
unordered_map<int, int> cd_map;//很关键的无序map
for (int c : nums1) {
for(int d :nums2){
cd_map[c + d]++;//
}
}
int num_tuple = 0;
int need;
for(int a:nums3){
for(int b: nums4){
need=0- a-b;
if(cd_map.find(need)!=cd_map.end()){
num_tuple+=cd_map[need];
}
}
}
return num_tuple;
}
};
06赎金信
思路
这题想的是如何优化达到最小的时间复杂度,如果用数组存杂志,那么复杂度为M+2R如果用数组存赎金信那么复杂度为R+2M+26,在小样本下二者差不多
Code
class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
// if (ransomNote.size() > magazine.size()) return false;
int record[26] = {0};
for (auto s : magazine) {
record[s - 'a']++;
}
for (auto s : ransomNote) {
record[s - 'a']--;
if (record[s - 'a'] < 0) {
return false;
}
}
return true;
}
};
07三数之和四数之和
Problem: 15. 三数之和
思路
这题难度很大,主要是双指针法不够熟悉。
- 固定一个指针,另外两个指针从左右两端开始往里靠,直到left==right
- 指针j设置为i+1,k设置为size-1,如果和大于0则右指针往左移动到下一个有效数字(不与上一位相同),如果小于0则left++到与left-1不重复的数字。如果等于0则两指针同时收缩到有效数字。
- 去重:去掉已经处理过的情况,比如i++之后的情况与i相同,那么就再i++
复杂度
时间复杂度:
添加时间复杂度, 示例:
空间复杂度:
添加空间复杂度, 示例:
Code
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
int i, left, right;
sort(nums.begin(), nums.end());
vector<vector<int>> result;//vector二维矩阵
for (i = 0; i < nums.size(); i++) {
//跳过已经处理过的数字
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
left=i+1;
right=nums.size()-1;
while (left < right) {
if (nums[i] + nums[left] + nums[right] == 0) {
result.push_back(vector<int>{nums[i], nums[left], nums[right]});
//找到答案之后同时收缩
left++;
right--;
//移动到下一个有效的指针
while (right > left && nums[right] == nums[right + 1]) right--;//移除0,0,2,2这类答案
while (right > left && nums[left] == nums[left - 1]) left++;//移除0,0,2,2这类答案
}
if (nums[i] + nums[left] + nums[right]>0){
right--;
}else if(nums[i] + nums[left] + nums[right]<0){
left++;
}
}
}
return result;
}
};
08四数之和
思路
这题思路和三数之和一样,嵌套了一个for循环,但是需要考虑到去重还有溢出的问题,所以十分复杂。
Code
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
int i, j, left, right;
sort(nums.begin(), nums.end());
vector<vector<int>> result;
if(nums.size()<4){return result;}
for (i = 0; i < nums.size() - 3; i++) {
if(i>0 && nums[i]==nums[i-1]){continue;}//处理过的就不再处理
for (j = i + 1; j < nums.size() - 2; j++) {
if(j>(i+1) && nums[j]==nums[j-1]){continue;}
left = j + 1;
right = nums.size() - 1;
while (left < right) {
if (target == (long)nums[i] + nums[j] +nums[left] + nums[right]) {
result.push_back(
{nums[i], nums[j], nums[left], nums[right]});
left++;
right--;
while (left < right && nums[left] == nums[left - 1])
left++;
while (left < right && nums[right] == nums[right + 1])
right--;
}
if (target < (long long ) nums[i] + nums[j] + nums[left] + nums[right]) {
right--;
}
if (target > (long long ) nums[i] + nums[j] + nums[left] + nums[right]) {
left++;
}
}
}
}
return result;
}
};