博客主页: A_SHOWY
系列专栏:力扣刷题总结录 数据结构 云计算
242. 有效字母的异位词 | easy | 哈希映射(数组),排序算法 |
383. 救赎金 | easy | 暴力,两个for循环两遍,哈希和242题基本一样 |
349. 两个数组的交集 | easy | 使用数组或者集合都可以,数组更快 |
202. 快乐数 | easy | 取出来每一位的操作要熟悉 |
1. 两数之和 | easy | 使用map要注意pair,auto |
454.四数相加Ⅱ | mid | map |
哈希表是根据关键码的值直接访问的数据结构,但是其实数组就是一张哈希表,哈希表的关键码就是所谓的索引下标,通过下标可以直接访问元素。
哈希表一般解决的问题是:快速判断一个元素是否出现在集合中。
枚举的话时间复杂度是o(n),但是使用哈希表的话时间复杂度是o(1)。
以查询学生的名字是否在学校里为例,把学生的姓名直接映射到哈希表上的索引,然后查询下标就可以知道他是否在学校
当哈希函数完成后,小李和小王都映射到了索引下标为1的位置,叫做哈希碰撞。那么哈希碰撞的解决方法主要有
以小陈和小国为例子,假如找到小陈和小国的索引都是1,则发生了冲突,那么冲突的元素都存在链表中 。
链表法需要注意选择适当的哈比表的大小,一来不会因为数组空值浪费太多内存,同时也不能让这个指针太长。
一定要保证数据规模小于哈希表的长度,需要哈希表的空位来解决碰撞的问题。
当使用哈希表来解决问题的时候,要用到三种数据结构
1.数组2.集合(set)3.映射(map)
当我们要快速查找一个元素的时候使用哈希表,但是哈希表也是牺牲了空间换取时间,因为我们要使用额外的数组,集合和映射存放数据。
242. 有效的字母异位词https://leetcode.cn/problems/valid-anagram/1.当我们看到数据很少,而且固定(限制数组的大小)时,当我们确定使用哈希表来解决问题时,优先使用数组来解决,是最省时间的。本题就是使用数组的哈希表最经典的一个题目
2.整体思路:判断是否是异位词,给了两个字符串,如果使用哈希表,且前提条件是确定是小写字母,那么先定义一个哈希数组不用记住a-z的ASCII码,直接s【i】- ‘a’做一个映射就可以,先对第一个字符串遍历一遍,如果出现a-z,对相应的哈希数组下标++。再对第二个字符串--,最后判断哈希数组中是否所有元素都是0
class Solution {
public:
bool isAnagram(string s, string t) {
//因为元素比较少,所以直接考虑用哈希的数组形式
int hash[26] = {0};
for(int i = 0;i < s.length(); i++)
{
hash[s[i] - 'a']++;
}
for(int i = 0; i < t.length(); i++)
{
hash[t[i] - 'a']--;
}
for(int i = 0; i<26; i++)
{
if(hash[i] != 0 ) return false;
}
return true;
}
};
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;
}
};
排序算法同样可以解决这道题目,先看一下字符数量是否一致,然后排序后看是否相同。但是时间复杂度稍大o(nlogn),哈希映射的时间复杂度为o(n)。
383. 赎金信https://leetcode.cn/problems/ransom-note/首先用暴力算法可以用两个for遍历两遍也可以通过
1.注意使用erase会消耗很多时间,同时字符串的erase写法后边括号里的是第几位
ransomNote.erase(ransomNote.begin() + j);//ransonNote删除出现过的这个字符
class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
for(int i = 0; i < magazine.length(); i++){
for(int j = 0; j < ransomNote.length(); j++){
if(magazine[i] == ransomNote[j]){
ransomNote.erase(ransomNote.begin() + j);//ransonNote删除出现过的这个
break;
}
}
}
if(ransomNote.length() == 0){
return true;
}
return false;
}
};
仔细观察这个题,其实和前面做到的有效字母的异位词很像,那个题目是两个互相包含,而这个只需要一个包含一个
class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
int hash[1000] = {0};
//先判断个数
if(magazine.length() < ransomNote.length()) return false;
for(int i = 0; i < magazine.length(); i++)
{
hash[magazine[i] -'a'] ++;
}
for(int j =0; j < ransomNote.length(); j++)
{
hash[ransomNote[j] - 'a'] --;
if(hash[ransomNote[j] - 'a'] < 0) return false;
}
return true;
}
};
349. 两个数组的交集https://leetcode.cn/problems/intersection-of-two-arrays/
1.确定用set以后,要求效率高,且去重所以使用unordered_set
2.代码理解上有一定难度for(int num : nums2)//指的是nums2中每个元素依次赋值给num
3.if(nums_set.find(num) != nums_set.end() result_set.insert(num);这一段是查是否有相交的部分,如果在nums_set中找到了num,那么就放到result里面。
4.注意这道题目要求的是返回数组return vector
class Solution {
public:
vector intersection(vector& nums1, vector& nums2) {
//先创建两个容器
unordered_set result_set;//存放结果
unordered_set nums_set(nums1.begin(),nums1.end());//将nums1存放到哈希表中
for(int num : nums2)
{
if(nums_set.find(num) != nums_set.end())
result_set.insert(num);
}
//看好返回的是数组
return vector(result_set.begin(),result_set.end());
}
};
1.当题目中给出了确定的范围1000的时候,其实这道题目更适合用数组哈希表来解决,效率更高,但是存储result为了去重,依然使用unordered_set。先将nums1给num,存到哈希表里,再检查nums2即可。
class Solution {
public:
vector intersection(vector& nums1, vector& nums2) {
unordered_set result_set;//存放结果
int hash[1005] = {0};
for(int num : nums1)
{
hash[num] = 1;
}
for(int num :nums2)
{
if(hash[num] ==1) result_set.insert(num);
}
return vector(result_set.begin(),result_set.end());
}
};
1.题目的整体思路为:题目说明了可能出现循环,当出现循环的时候,就是死循环了,核心还是把这个每一次的sum存到一个哈希表里,然后当重复的时候就用循环,因为不能重复,当重复时候就是死循环所以还是用unordered_set。
2.这里要非常熟悉把每一位取出来的操作
int getsum(int n)
{
int sum = 0;
while(n)
{
sum += (n % 10) * (n % 10);
n /= 10;
}
return sum;
}
3.后续的操作就是经典的集合哈希表找重复的过程
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 set;
while(1)
{
int sum = getsum(n);//注意这里要重新定义sum,因为是新函数
if(sum == 1) return true;
if(set.find(sum) != set.end()) return false;
else set.insert(sum);
n = sum;
}
}
};
1. 两数之和https://leetcode.cn/problems/two-sum/
1.我们第一个是要考虑元素是否出现过,同时还要考虑下标,所以要用map,但是我们的核心是要找元素是否出现过,所以把元素作为key,下标作为val。map用来存放遍历过的元素
2.当用map存数据的时候,要考虑两个属性,一个是key值,一个是val值。当我我们输出的时候,要用pair把键值对两个值合成一个进行输出
map.insert(pair(nums[i],i));
3.值得注意的是,当我们在map里面找这个元素的时候,返回的是一个迭代器,但是试图将它赋值成int的话,两边类型不一致就会报错,所以使用auto关键字可以将右侧的表达式map.find(s)推导出迭代器的实际类型并赋值给int变量
auto iter = map.find(s);
4.这个题目值得补充的地方很多,在map中,iter-first表示的是这个元素的key值,iter-second表示的是这个元素的val值。
5.注意区分length,length()和size()的区别
(1)length属性:用来获取数组长度
int a = ar.length;
(2)length( )方法:用来获取字符串的长度
(3)size( )方法:获取泛型集合中有多少元素个数,例如数组的个数
for(int i = 0; i < nums.size(); i++)
class Solution {
public:
vector twoSum(vector& nums, int target) {
unordered_map map;//先做一个map存数据
for(int i = 0; i< nums.size(); i++)
{
int s = target - nums[i];
auto iter = map.find(s);
if(iter != map.end())
return {iter -> second,i};
else map.insert(pair(nums[i],i));
}
return {};
}
};
454. 四数相加 IIhttps://leetcode.cn/problems/4sum-ii/
1.这个题目给了四个数组,如果直接暴力那么复杂度为o(n4)那么我们考虑将A和B数组的和为一组,C+D的和为一组进行存放后相加。那么除了要存放A+B的和还要存放出现的次数,所以要使用map哈希表。
2。我们考虑将A+B两个数组作为一个整体,C+D两个数组作为一个整体。A和B数组的和相加存储后,假如说a【i】+b【j】 = n(某个数)出现了三次,当我们在c+d= -5时,那么就出现了n个满足条件的四元组,count就加n。
3.在c++中指的是将a+b的值直接映射到map 的key中,如何存在则++,如果不存在就映射过去
map [a + b] ++;
class Solution {
public:
int fourSumCount(vector& nums1, vector& nums2, vector& nums3, vector& nums4) {
unordered_map map;
for(int a : nums1)
{
for(int b : nums2)
{
map [a + b] ++;//指的是将a+b的值直接映射到map 的key中,如何存在则++,如果不存在就映射过去
}
}
int count = 0;//初始化计数
for(int c : nums3)
{
for(int d : nums4)
{
if(map.find(0-c-d) != map.end())
{
count += map[-c-d];//这里需要注意,出现了几次,需要加上那个val不是加1
}
}
}
return count;
}
};