左维护,右枚举

左维护,右枚举 算法思想:
“左枚举,右维护”是一种在算法设计中常用的思想,特别是在处理需要动态维护某些状态或数据结构的问题时。这种思想可以概括为:

1.左枚举(Enumeration on the Left)

  • 指的是在算法(相对的,比如说:第一个数先进容器,第二个数后进容器,第一个数就在第二个数左边,第二个数就在第一个数右边)的一侧(通常是左侧),对所有可能的选择或情况进行枚举或遍历。

  • 枚举的过程中,会尝试每一种可能性,并针对每种情况产生一些候选的解决方案或中间结果。

2.右维护(Maintenance on theRight):

  • 指的是在算法的另一侧(通常是右侧),对枚举过程中产生的候选解决方案进行维护、更新或筛选。

  • 维护的目的是为了保持数据结构的有效性,或者是为了更新某些状态,以便能够快速响应后续的查询或操作。

大白话总结:用一个容器存储所有的数据,枚举左边并加入到容器中(不对其任何操作,如:改变其数值大小),维护右边满足条件的数值(通常和右边数值结合起来形成条件)进行统计、更新或筛选。以下灵神题单进行练习

1. 两数之和

class Solution {
    public int[] twoSum(int[] nums, int target) {
       Map<Integer,Integer> mp=new HashMap<Integer,Integer>();
       for(int i=0;i<nums.length;i++){
        if(mp.containsKey(target-nums[i])){
            return new int[]{mp.get(target-nums[i]),i};
        }
        mp.put(nums[i],i);
       }
       return new int[0];
    }
}

总结:题目含义是:左边+右边=target。利用哈希表一次次将数组左边元素存入,满足target-右边的数将其下标和左边数的下标存入到数组中。
1512. 好数对的数目

class Solution {
    public int numIdenticalPairs(int[] nums) {
     Map<Integer, Integer> cnt = new HashMap<>();
        int num= 0;
        for (int i = 0; i < nums.length; i++) {
            num+= cnt.getOrDefault(nums[i], 0);
            cnt.put(nums[i], cnt.getOrDefault(nums[i], 0) + 1);
        }
        return num;
    }
}

​总结:题目含义是一组数字 (i,j) 满足 nums[i] == nums[j] 且 i < j的个数。也是利用哈希表统计数组元素出现的次数,再用一个num变量统计好数对的数组。巧妙得地方就在于num加的值,第一次出现不记录是0,第二次出现记录好子数对加1。 对于数组 nums每个元素,它会增加 num 的值,增加的数量是该元素之前出现的次数。这样,当遇到相同的元素时,就会统计出之前的所有相同元素可以与当前元素形成多少对。最后返回 num,它包含了所有相同元素形成的数对。不用结合组合数学
219. 存在重复元素 II

class Solution {
    public boolean containsNearbyDuplicate(int[] nums, int k) {
        Set<Integer>  sum= new HashSet<>();
        for(int i = 0; i < nums.length; i++) {
            if(sum.contains(nums[i]))return true;
            sum.add(nums[i]);
            if(sum.size() > k)sum.remove(nums[i - k]);
        }
        return false;
    }
}

总结: 题目含义:是否存在两个 不同的索引 i 和 j ,满足 nums[i] == nums[j] 且 abs(i - j) <= k。为什么用set不用map,因为其自带去重的功能呀。直接从左到右遍历数组将元素存到set中,再统计set集合大小就啦!!!
121. 买卖股票的最佳时机

class Solution {
    public int maxProfit(int[] prices) {
        int sum = 0;
        int num=prices[0];
        for (int i=1;i<prices.length;i++) {
            num=Math.min(num,prices[i]);
            sum=Math.max(sum, prices[i]-num);
        }
        return sum;
    }
}

总结:题目转义:在数组左边找一个数字减去右边的数字的最大值,用num来记录左边最小值,sum来记录最大值。
1014. 最佳观光组合

class Solution {
    public int maxScoreSightseeingPair(int[] values) {
        int num=0,arr=values[0]+0;
        for(int j=1;j<values.length;j++){
            num=Math.max(num,arr+values[j]-j);
            arr=Math.max(arr,values[j]+j);
        }
        return num;
    }
}

跟上一题样,不就多加了一个字母嘛!!!
1010. 总持续时间可被 60 整除的歌曲

class Solution {
    public int numPairsDivisibleBy60(int[] time) {
        int num=0;
        int[] arr=new int[60];
        for(int i=0;i<time.length;i++){
            num+=arr[(60-time[i]%60)%60];
            arr[time[i]%60]++;
        }
        return num;
    }
}

总结:题目含义:下标数字 i 和 j 满足 i < j 且有 (time[i] + time[j]) % 60 == 0。用了一丢丢数学知识,(60-time[i]%60)%60充当算j的个数满足题意的数字对个数。(若循环内部两个语句调换位置则不满足 i < j 条件)
3185. 构成整天的下标对数目 II

class Solution {
    public long countCompleteDayPairs(int[] hours) {
        long num=0;
        int[] arr=new int[24];
        for(int i=0;i<hours.length;i++){
            int h=hours[i]%24;
            num+=arr[(24-h)%24];
            arr[h]++;
        }
        return num;
    }
}

跟上一题一样,就不总结了哈。
930. 和相同的二元子数组

class Solution {
    public int numSubarraysWithSum(int[] nums, int goal) {
         HashMap<Integer,Integer> map = new HashMap<>();
         map.put(0, 1);//垫底防止误差,若不写的话会少算一次滴
        int count = 0;
        int presum = 0;
        for (int x : nums) {
            presum += x;
            //当前前缀和已知,判断是否含有 presum - k的前缀和,那么我们就知道某一区间的和为k了。
            if (map.containsKey(presum - goal)) {
                count += map.get(presum - goal);
            }
            map.put(presum,map.getOrDefault(presum,0)+1);
        }
        return count;
    }
}

总结:和前缀和结合起来,还是不算难哈哈哈哈哈。
560. 和为 K 的子数组

class Solution {
    public int subarraySum(int[] nums, int k) {
         HashMap<Integer,Integer> map = new HashMap<>();
         map.put(0, 1);
        int count = 0;
        int presum = 0;
        for (int x : nums) {
            presum += x;
            if (map.containsKey(presum - k)) {
                count += map.get(presum - k);
            }
            map.put(presum,map.getOrDefault(presum,0) + 1);
        }
        return count;
    }
}

跟上一题一模一样就换个字母,总结的话就算了吧~
最后一题的话这个写个别的吧—前缀和,感觉下面这套题挺具有代表性的
1310. 子数组异或查询

class Solution {
    public int[] xorQueries(int[] arr, int[][] queries) {
        int[] num=new int[arr.length+1];
        for(int i=0;i<arr.length;i++)num[i+1]=num[i]^arr[i];
        int[] cnt=new int[queries.length];
        for(int i=0;i<queries.length;i++)cnt[i]=num[queries[i][0]]^num[queries[i][1] + 1];
        return cnt;
    }
}

总结:这道题就用一个数组算前缀和一个数组算结果,而且还用到一丢丢位运算的基础知识哈哈哈。
第一次写blog,完结撒花~~~
若您有关于本文的其他想法,欢迎在评论区分享您的观点。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值