滑动窗口系列(相向双指针)/9.6

一、数组种的k个最强值

给你一个整数数组 arr 和一个整数 k 。设 m 为数组的中位数,只要满足下述两个前提之一,就可以判定 arr[i] 的值比 arr[j] 的值更强:

  •  |arr[i] - m| > |arr[j] - m|
  •  |arr[i] - m| == |arr[j] - m|,且 arr[i] > arr[j]

请返回由数组中最强的 k 个值组成的列表。答案可以以 任意顺序 返回。

思路:

最强值比较的规则为:和中位数的差值越大的最强。如果差值相同,大于中位数的强。

我们可以先排序,然后相向双指针,依次和中位数进行比较。

mid-nums[left]     nums[right]-mid  比较这两个数字的大小

代码:
class Solution {
    public int[] getStrongest(int[] arr, int k) {
        Arrays.sort(arr);
        int[] res = new int[k];
        int left = 0;
        int right = arr.length - 1;
        int mid = arr[right / 2];
        while (k>0) {
            if (mid - arr[left] > arr[right] - mid)
                res[--k] = arr[left++];
            else if (mid - arr[left] <= arr[right] - mid)
                res[--k] = arr[right--];
        }
        return res;
    }
}

二、找到k个最接近的元素(相向双指针/固定长度的滑动窗口)

相向双指针(删除法):
思路:

因为要找到k个最接近的元素,所以要删除掉len-k个,那么删除掉哪些元素呢?

又因为数组是一个非递减的。

如果目标值x更接近左边,那么右边就要多删除一点;

如果目标值x更接近右边,那么左边就要多删除一点。

直到删除后的right-left+1==k

代码:
class Solution {
    public List<Integer> findClosestElements(int[] arr, int k, int x) {
        int left=0;
        int right=arr.length-1;
        List<Integer> res=new ArrayList<>();
        while(right-left+1!=k){
            if(Math.abs(arr[left]-x)<=Math.abs(arr[right]-x)){
                right--;
            }else{
                left++;
            }
        }
        for(int i=left;i<=right;i++){
            res.add(arr[i]);
        }
        return res;
    }
}
固定长度的滑动窗口:
思路:

因为元素的数目是固定的,所以滑动窗口的长度也是固定的。

从左往右遍历数组,当滑动窗口的长度==k的时候,如果此时相差值的和是更小的。那么就更新l,l,r。

代码:
class Solution {
    public List<Integer> findClosestElements(int[] arr, int k, int x) {
        //最接近的k个元素一定是一个连续子数组 所以也是一个滑动窗口
        int left=0;
        int right=0;
        int sum=0;
        int l=0;
        int r=0;
        int res=Integer.MAX_VALUE;
        while(right<arr.length){
            sum+=Math.abs(arr[right]-x);
            if(right-left+1==k){
                if(sum<res){
                    res=sum;
                    l=left;
                    r=right;
                }
                sum-=Math.abs(arr[left]-x);
                left++;
            }
            right++;
        }
        List<Integer> ans=new ArrayList<>();
        for(int i=l;i<=r;i++){
            ans.add(arr[i]);
        }
        return ans;
    }
}

三、数的平方等于两数乘积的方法数

题意:

给你两个整数数组 nums1 和 nums2 ,请你返回根据以下规则形成的三元组的数目(类型 1 和类型 2 ):

  • 类型 1:三元组 (i, j, k) ,如果 nums1[i]2 == nums2[j] * nums2[k] 其中 0 <= i < nums1.length 且 0 <= j < k < nums2.length
  • 类型 2:三元组 (i, j, k) ,如果 nums2[i]2 == nums1[j] * nums1[k] 其中 0 <= i < nums2.length 且 0 <= j < k < nums1.length
代码:
class Solution {
    public int numTriplets(int[] nums1, int[] nums2) {
        return calCount(nums1, nums2) + calCount(nums2, nums1);
    }

    public int calCount(int[] nums1, int[] nums2) {
        int res = 0;
        for (int num : nums1) {
            long square = (long) num * num;
            Map<Long, Integer> productMap = new HashMap<>();
            // 遍历 nums2 中的每对元素,寻找满足条件的三元组
            for (int j = 0; j < nums2.length; j++) {
                if (square % nums2[j] == 0) { 
                    long complement = square / nums2[j];
                    res += productMap.getOrDefault(complement, 0);
                }
                // 更新当前 nums2[j] 的频次
                productMap.put((long) nums2[j], productMap.getOrDefault((long) nums2[j], 0) + 1);
            }
        }

        return res;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值