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

【Leetcode算题记录】枚举技巧(枚举右,维护左)

枚举技巧–枚举右,维护左

枚举右,维护左是一种算法思想,用于处理需要动态维护状态或数据结构的问题。其核心是将问题分解为两个部分:

  • 枚举右:在算法的一侧(通常是右侧),枚举或遍历所有可能的选择或情况,产生候选解决方案或中间结果。
  • 维护左:在算法的另一侧(通常是左侧),对候选解决方案进行维护、更新或筛选,以保持数据结构的有效性或更新状态,快速响应后续操作。

这种思想常用于解决双变量问题,例如在数组中寻找和为目标值的数对。通过枚举左侧元素,利用某种数据结构维护右侧元素的信息,可以高效地找到符合条件的数对。

最典型的一道运用这一枚举技巧的题目是1. 两数之和


1. 两数之和

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。你可以假设每种输入只会对应一个答案,并且你不能使用两次相同的元素。你可以按任意顺序返回答案。

这道题最直观的方法就是两重循环遍历所有可能的组合,时间复杂度是 O ( n 2 ) O(n^2) O(n2),采用枚举左,维护右的技巧可以将时间复杂度降为 O ( n ) O(n) O(n)。题目要求找到一对下标满足 n u m s [ i ] + n u m s [ j ] = t a r g e t nums[i]+nums[j]=target nums[i]+nums[j]=target,那么可以将这个式子变型,也就是 n u m s [ i ] = t a r g e t − n u m s [ j ] nums[i]=target-nums[j] nums[i]=targetnums[j],显然枚举右就是枚举每一个 n u m s [ j ] nums[j] nums[j]并将下标一起保存到哈希表中,在 n u m s [ j ] nums[j] nums[j]的左边查找是否存在 n u m s [ i ] = t a r g e t − n u m s [ j ] nums[i]=target-nums[j] nums[i]=targetnums[j]

代码

vector<int> twoSum(vector<int>& nums, int target) {
    unordered_map<int,int> mp;
    for(int i=0;i<nums.size();i++){
        if(mp.find(target-nums[i])!=mp.end()){
            return {mp[target-nums[i]],i};
        }
        mp[nums[i]]=i;
    }
    return {};
}

这段代码中要注意的地方就是查找左边的数和将枚举右边的数加入哈希表的顺序,由于题目要求不能使用两次相同的元素,所以不能先将枚举右边的数加入哈希表中,不然在查找左边元素的时候就有可能使用到刚刚枚举的元素了。

219. 存在重复元素 II

给你一个整数数组 nums 和一个整数 k ,判断数组中是否存在两个 不同的索引 ij ,满足 nums[i] == nums[j]abs(i - j) <= k 。如果存在,返回 true ;否则,返回 false

📑枚举技巧,维护左枚举右

这道题采用维护左,枚举右的枚举技巧,在遍历数组nums的时候,用一个哈希表mp记录元素x上一次出现的位置,并在x的左边查找是否出现过且满足当前位置j和上一次出现的位置i(mp[nums[j]])的差满足题目条件。

bool containsNearbyDuplicate(vector<int>& nums, int k) {
    unordered_map<int,int> mp;
    for(int i=0;i<nums.size();i++){
        if(mp.contains(nums[i])&&i-mp[nums[i]]<=k){
            return true;
        }
        mp[nums[i]]=i;
    }
    return false;
}

这里同样要注意枚举右和维护左的顺序,题目要求了不同的索引,所以这里先在左边查找,再将枚举的右边元素加入到哈希表中。

除此之外,这道题还可以通过维护一个定长为k+1的滑动窗口解决。

对于一个长度为k+1的窗口,窗口中任意一个索引的差值的绝对值都不会超过k,那么我们就只需要遍历所有长度为k+1的窗口,判断窗口中是否存在重复元素即可。

具体操作就是用哈希集合维护窗口,从左到右遍历数组nums,当遍历到下标为i时:

  1. 如果 i>k,则下标i−k−1处的元素被移出滑动窗口,因此将nums[i−k−1]从哈希集合中删除;
  2. 判断 nums[i] 是否在哈希集合中,如果在哈希集合中则在同一个滑动窗口中有重复元素,返回 true,如果不在哈希集合中则将其加入哈希集合。

代码

bool containsNearbyDuplicate(vector<int>& nums, int k) {
    unordered_set<int> s;
    int n=nums.size();
    for(int i=0;i<n;i++){
        if(i>k){
            s.erase(nums[i-k-1]);
        }
        if(s.count(nums[i])){
            return true;
        }
        s.emplace(nums[i]);
    }
    return false;
}

1512. 好数对的数目

给你一个整数数组 nums 。如果一组数字 (i,j) 满足 nums[i] == nums[j]i < j ,就可以认为这是一组 好数对 。返回好数对的数目。

在遍历数组nums时,用哈希表cnt保存当前遍历过的每个数字及其出现的次数,对于每个元素nums[j],其之前出现的次数即为与之构成好数对的数量,将这些数量累加到结果ans中,并更新nums[j]在哈希表cnt中的计数,最终返回累加的好数对总数ans。

int numIdenticalPairs(vector<int>& nums) {
    unordered_map<int,int>cnt;
    int ans=0;
    for(int i=0;i<nums.size();i++){
        ans+=cnt[nums[i]]++;

    }
    return ans;
}

代码中为什么先累加ans再更新哈希表?
如果先更新哈希表再累加好数对,那么我们在查看哈希表时得到的就是包括当前元素在内的总出现次数,这会导致我们将当前元素与自己也计算为一个好数对(即i == j的情况,但题目要求i < j),这不符合题目要求。

1014. 最佳观光组合

给你一个正整数数组 values,其中 values[i] 表示第 i 个观光景点的评分,并且两个景点 ij 之间的 距离j - i。一对景点(i < j)组成的观光组合的得分为 values[i] + values[j] + i - j ,也就是景点的评分之和 减去 它们两者之间的距离。

返回一对观光景点能取得的最高分。

得分 r e s = v a l u e s [ i ] + v a l u e s [ j ] + i − j res=values[i]+values[j]+i-j res=values[i]+values[j]+ij可以对这个式子稍加变一下型 r e s = ( v a l u e s [ i ] + i ) + ( v a l u e s [ j ] − j ) res=(values[i]+i)+(values[j]-j) res=(values[i]+i)+(values[j]j)对于这个式子,可以枚举j,同时维护在j左边 v a l u e s [ i ] + i values[i]+i values[i]+i的最大值left_max,用 l e f t _ m a x + v a l u e s [ j ] − j left\_max+values[j]-j left_max+values[j]j来更新res的最大值。

int maxScoreSightseeingPair(vector<int>& values) {
    int left_max=INT_MIN,res=0;
    for(int i=0;i<values.size();i++){
        res=max(res,left_max+values[i]-i);
        left_max=max(left_max,i+values[i]);
    }
    return res;
}

http://www.kler.cn/a/527482.html

相关文章:

  • 深度学习的应用
  • 【C++高并发服务器WebServer】-9:多线程开发
  • 【Block总结】OutlookAttention注意力,捕捉细节和局部特征|即插即用
  • Leetcode:219
  • AI大模型开发原理篇-8:Transformer模型
  • 【重生之我在学习C语言指针详解】
  • VisionMamba安装
  • Java小白入门教程:三种注释+快捷方式
  • 三傻排序的比较(选择,冒泡,插入)
  • C++——类和对象(下)
  • js基础(黑马)
  • 基于Scrapy采集豆瓣电影Top250的详细数据
  • Java小白入门教程:类?方法?变量?
  • 【LLM-agent】(task1)简单客服和阅卷智能体
  • Hugging Face 推出最小体积多模态模型,浏览器运行成为现实!
  • 学习Python编程,需要哪些编程语言基础?如何开始学习Python?
  • 概述、 BGP AS 、BGP 邻居、 BGP 更新源 、BGP TTL 、BGP路由表、 BGP 同步
  • Python微服务框架Nameko | python 小知识
  • 实现使用K210单片机进行猫脸检测,并在检测到猫脸覆盖屏幕50%以上时执行特定操作
  • Koa 基础篇(二)—— 路由与中间件
  • 事务04之死锁,锁底层和隔离机制原理
  • 【C++语言】卡码网语言基础课系列----4. A+B问题IV
  • 使用 Tauri 2 + Next.js 开发跨平台桌面应用实践:Singbox GUI 实践
  • Flask 使用Flask-SQLAlchemy操作数据库
  • pytorch实现基于Word2Vec的词嵌入
  • 记一次将Java web服务部署上云的全过程