当前位置: 首页 > news >正文

3-25hash专训

3-25

hash训练

383.赎金信

1.思路:怎么直接比较两个字符串的字符出现的个数呢?用mp进行通过

  1. 用两个map统计出现次数,然后再进行统计
bool canConstruct(string ransomNote, string magazine) {
    ordered_map<int,int>mr,mp;
    for(char rs:ransomNote)
    {
        mr[rs]++;
    }
    for(char ms:magazine)
    {
        mm[ms]++;
    }
    for(char rs:ransomNote)
    {
        if(mm.count(rs)&&mr[rs]<=mm[rs]>)
        {
            continue;
        }else{
            return false;
        }
    }
    return true;
}

2.优化思路:做到边统计边比较?比如

先统计目标target的map

for(char rs:ransomNote)
{
    mr[rs]++;
}

然后在

for(char ms:magazine)
{
        mm[ms]++;
}

如果在这个过程中,发现某一个ms[mr]的次数已经小于了,表明已经正确了,但是这个整个的比较过程还是无法忽略的。因为还是要比到最后才能发现子串是否能够生成

3.换一个思路

先统计能够提供生成目标target的字符,在遍历ransomNote的时候去解决用mm[mr]–,如果发现了问题,比如mm[mr]已经等于零了,说明根本无法生成目标串。可以变成

bool canConstruct(string ransomNote, string magazine) {
    unordered_map<int,int>mm;
    for(char ms:magazine)
    {
        mm[ms]++;
    }
    for(char rs:ransomNote)
    {
        if(mm[rs])
        {
            mm[rs]--;
            continue;
        }else{
            return false;
        }
    }
    return true;
}

总结

首先,暴力解法过程出现了三次遍历,并且使用了两个空间来比较,所以这个是优化思路。
其次,对于判断资源是不是够的问题,如果我们能够先统计资源,然后再在遍历target的过程中去消耗资源,如果资源不够再报错只进行了两次遍历,一个空间复杂度来解决问题。

1.两数之和

示例 1:

输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。

示例 2:

输入:nums = [3,2,4], target = 6
输出:[1,2]

示例 3:

输入:nums = [3,3], target = 6
输出:[0,1]

提示:

2 <= nums.length <= 10e4
-10e9 <= nums[i] <= 10e9
-10e9 <= target <= 10e9
只会存在一个有效答案

1.思路:

分析题目,数组无序,可能出现重复,要求返回下标。并且如果是重复两个数之和要求返回不同下标。

方法1

vector<int> twoSum(vector<int>& nums, int target) {
    //思路:1. 我直接进行sort(nums.begin(),nums.end())
    //然后遍历nums,nums.find(target)得到迭代器,如果迭代器与当前值相同,那么迭代器进行++,如果相加为target就返回不同的下标
}

问题在于要两个下标,你sort后是下标变化了,如果是问你有没有两个值相加为target的上述代码可以

方法2

方法1的问题:首先sort改变了数组nums了,这个nums是引用传值,直接改变了数组,在大多情况下时不合适的。
所以,我们应该怎么解决这个问题呢?

  1. 首先,我们可以想到数组长度为10e4,完全可以开辟空间去解决问题。
  2. 那么我们想返回下标,并且可以随机进行查询,这个时候会想到map,而普通的map是基于红黑树的,查询速度慢,且保存了很多父子节点,以及颜色的数据,导致效率不高
  3. unordered_map是散列表,并且是无序的,而我们要求返回的数据却是有序的,怎么办呢?
  4. 这个时候,我们可以变遍历,边查询前面是否有值满足条件,然后返回遍历的i和map中前面保存的taraget-nums[i]的下标。所以代码为
vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map<int,int>mp;
        for(int i=0;i<nums.size();i++)
        {
            int need=target-nums[i];
            if(mp.count(need))
            {
                return {mp[need],i};
            }
            mp[nums[i]]=i;
        }
        return {};
    }

49异位字母

示例 1:

输入: strs = ["eat", "tea", "tan", "ate", "nat", "bat"]
输出: [["bat"],["nat","tan"],["ate","eat","tea"]]

示例 2:

输入: strs = [""]
输出: [[""]]

示例 3:

输入: strs = ["a"]
输出: [["a"]]

提示:

1 <= strs.length <= 104
0 <= strs[i].length <= 100
strs[i] 仅包含小写字母

1.思路

很类似于滑动窗口,让你找出满足条件的字符序列,所以我们可以引入滑动窗口比较的思路
对于

vector<vector<string>> groupAnagrams(vector<string>& strs) {
    //1.我们要保留滑动窗口曾经出现过的字符,比如先遍历eat,要保存起曾经出现过,并且要有一个数组进行存储{"eat"},并且后续出现"tea"也要给他存到对应的数组里面,那么什么方式允许这么做呢?那只有map了,同时我们对于数组的顺序没有要求,所以使用unordered_map<string,vector<string>>mp;
    //2.怎么比较eat和tea这个两个是正确的呢?我们直接通过sort排序字符,然后进行映射,但是要注意sort本身会改变原来的字符串,这样我们如果继续用原本的key去映射的话,会出现明显的问题,所以要用一个tmp去存储sort之前的字符串
     vector<vector<string>>res;
        unordered_map<string,vector<string>>mp;
        for(auto &s:strs)
        {
            string tmp=s;
            sort(s.begin(),s.end());
            if(mp.find(s)!=mp.end())
            {
                mp[s].emplace_back(tmp);
            }else{
                mp.insert({s,{tmp}});
            }
        }
        for(auto & m:mp)
        {
            res.emplace_back(m.second);
        }
        return res;
}
if(mp.find(s)!=mp.end())
{
        mp[s].emplace_back(tmp);
    }else{
        mp.insert({s,{tmp}});//insert本身也是会用move的右值进行构造的
}
可以更换为:
mp[s].emplace_back(tmp);//直接进行构造

128最长连续序列

示例 1:

输入:nums = [100,4,200,1,3,2]
输出:4
解释:最长数字连续序列是 [1, 2, 3, 4]。它的长度为 4。

示例 2:

输入:nums = [0,3,7,2,5,8,4,6,0,1]
输出:9

示例 3:

输入:nums = [1,0,1,2]
输出:3

提示:

0 <= nums.length <= 10e5
-10e9 <= nums[i] <= 10e9
int longestConsecutive(vector<int>& nums) {
    传入的同样是一个数组,长度不超过10e5,好像上面几题的空间复杂度都是这个条件,说明实践中,用hash不能占用太多的内存十万个,和redis的两级页查不了太多,如果一个算法占用太多内存肯定时不太好的
}

1.思路

开始的时候想的是一个先排序,然后用一个单调栈来记录长度,但是要求时间复杂度为O(N)
所以,不能排序,而且要时间复杂度为O(N),并且是需要遍历数组的,所以最好的方式就是用hash的方式,同时我们只是统计长度,和顺序无关,并且不需要返回这些数据的下标,所以我们可以选择用set,而要时间复杂度为O(N),红黑树的set无法满足要求,所以要用unorederd_set来存储并解决问题。
代码如下:

int longestConsecutive(vector<int>& nums) {
    unordered_set<int>st;
    for(auto&n:nums)
    {
        st.insert(n);
    }
    int res=0;
    for(const int& s:st)
    {
        if(st.find(s-1)==st.end())
        {
            int cur=s;
            int count=1;
            while(st.find(cur+1)!=st.end())
            {
                cur++;
                count++;
            }
            res=max(res,count);
        }
    }
     return res;
}

相关文章:

  • php写入\查询influxdb数据
  • HCIP NOTE_01_基础概念
  • GitHub和Gitee上的一些AI项目
  • springboot使用netty做TCP客户端
  • 使用Github项目nghttp2的样例学习HTTP/2
  • 【STM32】知识点介绍二:GPIO引脚介绍
  • stm32 外部中断实现
  • 26考研——图(6)
  • kafka学习
  • 登录验证码的接口实习,uuid,code.
  • 数据结构十五、排序
  • 【计算机网络编码与调制】
  • 2025年- G28-Lc102-973. K 个距离原点最近的点--java版
  • 适合开发点餐系统的PHP开源框架要具备哪些优势?
  • Java设计模式之迭代器模式
  • 强化学习与智能决策:基本原理、算法及应用
  • @JSONField(serialize = false)序列化过程中排除特定字段
  • 从零构建大语言模型全栈开发指南:第二部分:模型架构设计与实现-2.2.3实战案例:在笔记本电脑上运行轻量级LLM
  • NLP高频面试题(十六)——deepspeed原理
  • 记一次线上环境JAR冲突导致程序报错org.springframework.web.util.NestedServletException
  • 做网站 图片格式/石景山区百科seo
  • 长沙做网站一般要多少钱/查询网站收录
  • 外贸仿牌网站/中国免费网站服务器主机域名
  • 魅力网络营销公司/网站搜索优化技巧
  • 阿里云做网站需要环境/持啊传媒企业推广
  • 网站关闭多久排名会下降/友情链接查询结果