首页 > 其他分享 >图解LeetCode——658. 找到 K 个最接近的元素(难度:中等)

图解LeetCode——658. 找到 K 个最接近的元素(难度:中等)

时间:2023-05-23 11:05:57浏览次数:29  
标签:endIndex arr int 元素 midIndex startIndex 图解 LeetCode 658


一、题目

给定一个 排序好 的数组 arr ,两个整数 kx ,从数组中找到最靠近 x(两数之差最小)的 k 个数。返回的结果必须要是按升序排好的。

整数 a 比整数 b 更接近 x 需要满足:|a - x| < |b - x| 或者 |a - x| == |b - x| 且 a < b

二、示例

2.1> 示例 1:

【输入】arr = [1,2,3,4,5], k = 4, x = 3
【输出】[1,2,3,4]

2.2> 示例 2:

【输入】arr = [1,2,3,4,5], k = 4, x = -1
【输出】[1,2,3,4]

提示:

  • 1 <= k <= arr.length
  • 1 <= arr.length <= 10^4
  • arr 按 升序 排列
  • -10^4 <= arr[i],x <= 10^4

三、解题思路

3.1> 思路1:中心点+前后指针

根据题目描述,我们可以知道其中有几个重要的信息。首先:这个数组arr是排序好了的,并且要求返回的结果也是排序的。那么,我们可以推测出来,最终的结果也就是原数组arr的一个子集

那么,我们就可以先根据题目中给的查找值x,去确定一下所在数组arr的下标位置midIndex。但是在查找过程中,如果查找到了相同值还好办,如果没有查找到与x相同的值,那怎么办呢?这里我们可以通过x与数组arr中每个元素进行判断,如果我们第一次发现第i个元素大于等于x了,那么就说明,midIndex的值要么是i,要么就是i-1,具体取哪个值,我们可以通过判断i和i-1这两个元素与x的差值哪个小,我们就将midIndex指向该位置。

判断完毕midIndex的值之后,我们就可以以它为中心点,向左或者向右进行发散操作。最初我们可以创建两个变量,分别为开始索引startIndex和结束索引endIndex,最初这两个值与midIndex都是相同的。那么我们可以根据方法入参k(最终数组长度)来确定startIndex或endIndex要移动的次数,在每次移动操作之前,我们都要对比arr[startIndex - 1]和arr[endindex + 1]这两个元素,如果对比的结果是小于等于,那么startIndex向前移动一位,即:startIndex++;反之,如果对比的结果是大于,那么则endIndex向后移动一位,即:endIndex--;直到循环次数完毕,组装最终的结果作为方法的返回值。

上面就是具体的解题思路,下面我们依旧以一个例子来说明具体的执行步骤。我们假设arr数组为:arr=[0,1,1,1,2,3,6,7,8,9],需要返回数组长度:k=9,需要查找的元素值为x=4。那么,首先,我们遍历arr,当遍历到元素6的时候,第一次满足x < arr[i],那么我们对比元素6与它前一位元素3哪一个与x=4的差值最小,我们发现,元素3的差值更小,所以,我们指定midIndex=5(指向元素3的位置)。

然后,我们创建startIndex和endIndex,初始值就是midIndex的值,即:startIndex=endIndex=midIndex;然后在每次循环中,通过判断arr[startIndex - 1]和arr[endIndex + 1]这两个元素与x=4之前的差值,然后向更小差值的一放移动。当循环完毕后,最终结果的数组就是在arr数组中以startIndex为开始,以endIndex为结束的这段子集。具体操作如下图所述:

图解LeetCode——658. 找到 K 个最接近的元素(难度:中等)_后端

3.2> 思路2:排除无用元素

根据题意,逆向思考一下,其实我们不需要确定中间的元素在哪里,因为结果数组一定是连续的,所以只需要确定哪些元素对我们来说是“无用”的元素,然后将这些元素“排除在外”就可以了。

我们还是以arr数组为:arr=[0,1,1,1,2,3,6,7,8,9],需要返回数组长度:k=9,需要查找的元素值为x=4为例。首先,我们确定要“排除”的元素有多少个,计算方式就是:arr.length - k = 10 - 9 = 1。我们只需要排除掉一个元素即可,那么我们设定开始索引位置startIndex=0,结束索引位置endIndex= arr.length -1; 然后计算一下,x与arr[startIndex] 和 x与arr[endIndex]那个差值更大,那么就将差值大的那个元素,排除在外即可。具体操作如下图所示:

图解LeetCode——658. 找到 K 个最接近的元素(难度:中等)_后端_02

四、代码实现

4.1> 代码1:中心点+前后指针

class Solution {
    public List<Integer> findClosestElements(int[] arr, int k, int x) {
        int midIndex = 0; // 中心位置
        if (x <= arr[0]) return build(arr, 0, k, x);
        if (x >= arr[arr.length - 1]) return build(arr, arr.length - 1, k, x);
        for (int i = 0; i < arr.length; i ++) if (arr[i] >= x) return build(arr, (Math.abs(arr[i] - x)) >= (Math.abs(x - arr[i - 1])) ? i - 1: i, k, x);
        return new ArrayList();
    }

    public List<Integer> build(int[] arr, int midIndex, int length, int x) {
        int startIndex = midIndex, endIndex = midIndex;
        while(length > 1) {
            if ((endIndex >= arr.length - 1) || (startIndex > 0 && Math.abs(x - arr[startIndex - 1]) <= Math.abs(arr[endIndex + 1] - x))) startIndex--;
            else endIndex++;
            length--;
        }
        List<Integer> result = new ArrayList<>();
        for (int i = startIndex; i <= endIndex; i++) result.add(arr[i]);
        return result;
    }
}

图解LeetCode——658. 找到 K 个最接近的元素(难度:中等)_Math_03

4.2> 代码2:排除无用元素

class Solution {
    public List<Integer> findClosestElements(int[] arr, int k, int x) {
        int deleteNum = arr.length - k, startIndex = 0, endIndex = arr.length - 1;
        while (deleteNum > 0) {
            if ((x - arr[startIndex]) <= (arr[endIndex] - x)) endIndex--;
            else startIndex++;
            deleteNum--;
        }

        List<Integer> result = new ArrayList<>();
        for (int i = startIndex; i <= endIndex; i++) {
            result.add(arr[i]);
        }
        return result;
    }
}

图解LeetCode——658. 找到 K 个最接近的元素(难度:中等)_后端_04

今天的文章内容就这些了:

写作不易,笔者几个小时甚至数天完成的一篇文章,只愿换来您几秒钟的 点赞 & 分享

更多技术干货,欢迎大家关注公众号“爪哇缪斯” ~ \(^o^)/ ~ 「干货分享,每天更新」

标签:endIndex,arr,int,元素,midIndex,startIndex,图解,LeetCode,658
From: https://blog.51cto.com/u_15003301/6330107

相关文章

  • 图解LeetCode——剑指 Offer 36. 二叉搜索树与双向链表
    一、题目输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的循环双向链表。要求不能创建任何新的节点,只能调整树中节点指针的指向。二、示例为了让您更好地理解问题,以下面的二叉搜索树为例:我们希望将这个二叉搜索树转化为双向循环链表。链表中的每个节点都有一个前驱和后继指针。对......
  • 图解LeetCode——782. 变为棋盘(难度:困难)
    一、题目一个 n*n 的二维网络 board 仅由 0 和 1 组成 。每次移动,你能任意交换两列或是两行的位置。返回将这个矩阵变为 “棋盘”  所需的最小移动次数 。如果不存在可行的变换,输出-1。“棋盘”是指任意一格的上下左右四个方向的值均与本身不同的矩阵。二、示例......
  • 图解LeetCode——1460. 通过翻转子数组使两个数组相等(难度:简单)
    一、题目给你两个长度相同的整数数组 target 和 arr 。每一步中,你可以选择 arr 的任意非空子数组 并将它翻转。你可以执行此过程任意次。如果你能让arr 变得与target 相同,返回True;否则,返回False。二、示例2.1>示例1:【输入】target=[1,2,3,4],arr=[2,4,1,3]......
  • 图解LeetCode——剑指 Offer 07. 重建二叉树
    一、题目输入某二叉树的前序遍历和中序遍历的结果,请构建该二叉树并返回其根节点。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。二、示例2.1>示例1:【输入】preorder=[3,9,20,15,7],inorder=[9,3,15,20,7]【输出】[3,9,20,null,null,15,7]2.2>示例2:【输入】pr......
  • 图解LeetCode——剑指 Offer 29. 顺时针打印矩阵
    一、题目输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。二、示例2.1>示例1:【输入】matrix=[[1,2,3],[4,5,6],[7,8,9]]【输出】[1,2,3,6,9,8,7,4,5]2.2>示例2:【输入】matrix= [[1,2,3,4],[5,6,7,8],[9,10,11,12]]【输出】[1,2,3,4,8,12,11,10,9,5,6,7]限......
  • 图解LeetCode——剑指 Offer 15. 二进制中1的个数
    一、题目编写一个函数,输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中数字位数为'1'的个数(也被称为 汉明重量)。二、示例2.1>示例1:【输入】n=11(控制台输入00000000000000000000000000001011)【输出】3【解释】输入的二进制串000000000000000000000000000010......
  • 图解LeetCode——654. 最大二叉树(难度:中等)
    一、题目给定一个不重复的整数数组 nums。 最大二叉树 可以用下面的算法从 nums递归地构建:1>创建一个根节点,其值为 nums中的最大值。2>递归地在最大值 左边 的 子数组前缀上 构建左子树。3>递归地在最大值右边的 子数组后缀上 构建右子树。返回 nums构建的......
  • 图解LeetCode——1224. 最大相等频率(难度:困难)
    一、题目给你一个正整数数组 nums,请你帮忙从该数组中找出能满足下面要求的最长前缀,并返回该前缀的长度:从前缀中恰好删除一个元素后,剩下每个数字的出现次数都相同。如果删除这个元素后没有剩余元素存在,仍可认为每个数字都具有相同的出现次数(也就是0次)。二、示例2.1>示例1:【......
  • 图解LeetCode——769. 最多能完成排序的块(难度:中等)
    一、题目给定一个长度为n的整数数组arr,它表示在[0,n-1]范围内的整数的排列。我们将arr分割成若干块(即分区),并对每个块单独排序。将它们连接起来后,使得连接的结果和按升序排序后的原数组相同。返回数组能分成的最多块数量。二、示例2.1>示例1:【输入】arr=[4,3,2,......
  • 图解LeetCode——940. 不同的子序列 II(难度:困难)
    一、题目给定一个字符串s,计算s的不同非空子序列的个数。因为结果可能很大,所以返回答案需要对10^9+7取余。字符串的子序列是经由原字符串删除一些(也可能不删除)字符但不改变剩余字符相对位置的一个新字符串。例如:"ace"是"abcde"的一个子序列,但"aec"不是。二、示例2.......