Leetcode - 352周赛详解

一,最长奇偶子数组

 看题可知可以使用暴力求解,从头开始遍历数组,另设一个变量来记录满足条件的子数组长度的最大值,将该变量不断与新得到的子数组长度比较,最终得到子数组长度的最大值。但是这样依次遍历的话,时间复杂度为O(n^2)。

设子数组的左下标为 left ,右下标为 right,在上面的解法中,如果一个子数组满足了条件,我们会重复计算[left+1,right]、[left+2,right]....[right,right] 这些子数组长度,因为我们要找到最长,所以这些子数组不用考虑,为了优化这一缺点,我们需要设置一个变量来存储子数组后面的第一个满足1,3条件的初始下标。优化后的时间复杂度为O(n),代码如下:

class Solution {
    public int longestAlternatingSubarray(int[] nums, int threshold) {
        int i = 0;
        int ans = 0;
        while(i < nums.length){
            if(nums[i]>threshold || nums[i]%2 != 0){//筛选子数组的初始下标
                i++;
                continue;
            }
            int j = i;//记录子数组的初始下标
            i++;//避免出现 i+1 使数组下标越界
            //注意:这里没有写nums[i-1]<=threshold的原因是,我们已经在上面比较过了!!!
            while(i < nums.length && nums[i] <= threshold && nums[i]%2 != nums[i-1]%2){
                i++;
            }
            ans = ans > i - j ? ans : i - j;
        }
        return ans;
    }
}

二,和等于目标值的质数对

 在解决这道题之前,我们需要了解埃式筛,埃式筛是一种得到质数的算法,它的操作是:遍历[2,n]的数,将2的倍数全部删除,下一个遍历的值是3,将3的倍数全部删除,下一个遍历的值是5(因为4已经被删除了),再将5的倍数全部删除,依次类推,最终剩下的就是[2,n]的质数。

埃式筛的核心原理就是:一个质数的因数只有 1 和 它本身!!!所以只要能遍历到,就说明它是质数。

算法实现:

    public final static int MK = (int)1e6+1;//题目要求n 的值在[1,10^6]
    public final static int[] prime = new int[100001];//用来储存质数
    public static final boolean[] is_prime = new boolean[MK];//用来判断下标i是不是质数,false为是质数,true为不是质数
    static{
        int k = 0;
        for(int i=2; i<MK; i++){
            if(!is_prime[i]){
                prime[k++] = i;
                // for(int j=i*i; j <= MK-1; j+=i){
                //   is_prime[j] = true;
                //}
                //上面的写法如果i过大,j会超出int的最大值,从而导致下标越界
                for(int j=i; j <= (MK-1) / i; j++)
                    is_prime[i*j] = true;
            }      
        }
    }

当我们得到[1,n]的质数后,这道题就很简单了,遍历质数数组,将 X 赋值为质数,Y = N - X,如果Y也在质数数组中就说明[X,Y]是符合要求的质数对,将其添加到链表中。结束条件是 X < Y,因为题目要求按照非递减顺序排序。代码如下:

class Solution {
    public final static int MK = (int)1e6+1;
    public final static int[] prime = new int[100001];
    public static final boolean[] is_prime = new boolean[MK];
    static{
        int k = 0;
        for(int i=2; i<MK; i++){
            if(!is_prime[i]){
                prime[k++] = i;
                for(int j=i; j <= (MK-1) / i; j++)
                    is_prime[i*j] = true;
            }      
        }
    }
    public List<List<Integer>> findPrimePairs(int n) {
        List<List<Integer>> ans = new ArrayList();
        if(n%2 != 0){
            if( n>4 && !is_prime[n-2]){
                List<Integer> tmp = new ArrayList();
                tmp.add(2);
                tmp.add(n-2);
                ans.add(tmp);
                return ans;
            }
            return ans;
        }
        for(int i = 0; i < prime.length; i++){
            int x = prime[i];
            int y = n - x;
            if(x > y)break;
            if(!is_prime[y]){
                List<Integer> tmp = new ArrayList();
                tmp.add(x);
                tmp.add(y);
                ans.add(tmp);
            }
        }
        return ans;
    }
}

注意:这里的 if 语句是为了剪枝,不然会超出时间限制,其原理是 奇数A = 奇数B + 偶数C ,且奇数A只能有一个质数对,因为只有2既为偶数,也为质数,所以如果奇数B(即 A-2)是质数,则直接返回[2,n-2],不是质数,就返回空。

三,不间断子数组

题目意思就是nums的不间断子数组要满足   子数组最大值 - 子数组最小值 <= 2

在写这道题之前,我们先举一个例子来理解一下题意,例如:nums = [ 5 , 4 , 2 , 4 ]

 但是这么做的时间复杂度为O(n^2) ,我们还可以优化,实际上当不满足条件时,我们只需要将 L 移动到当前子数组第二大值或者第二小值(第二大/小值离L近就移动到第二大/小值),依次类推,再画个图理解一下:

 

 在理解了这一点后,我们需要做的就是存储 [ L , R ] 的从大到小和从小到大的下标,然后不断的移动L,R就可以了。代码如下:

class Solution {
    public long continuousSubarrays(int[] nums) {
        int[] max = new int[nums.length];//存储子数组的从大到小的下标
        int[] min = new int[nums.length];//存储子数组的从小到大的下标
        int head = 0, headB = 0;
        int last = 0, lastB = 0;
        int L = 0;
        long ans = 0;
        for(int R=0; R<nums.length; R++){
            while(head < last && nums[max[last-1]]<nums[R]){
                last--;
            }
            while(headB < lastB && nums[min[lastB-1]]>nums[R]){
                lastB--;
            }
            a[last++] = R;
            b[lastB++] = R;
            while(head < last && headB < lastB && nums[max[head]]-nums[min[headB]] > 2){
                if(max[head]<min[headB]){
                    head++;
                }else{
                    headB++;
                }
                L = max[head] > min[headB] ? min[headB] : max[head];
            }
            ans += R - L + 1;
        }
        return ans;
    }
}

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一叶祇秋

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值