leetcode
leetcode 551 ~ 600
找到 K 个最接近的元素

找到 K 个最接近的元素

难度:

标签:

题目描述

代码结果

运行时间: 25 ms, 内存: 17.0 MB


/*
 * Approach:
 * 1. Use Java Streams to sort the array elements based on their distance to x.
 * 2. Then, sort by natural order if distances are the same.
 * 3. Limit the result to k elements and return them as a sorted list.
 */
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
 
public class Solution {
    public List<Integer> findClosestElements(int[] arr, int k, int x) {
        return Arrays.stream(arr)
            .boxed()
            .sorted((a, b) -> {
                int diff = Math.abs(a - x) - Math.abs(b - x);
                if (diff == 0) return Integer.compare(a, b);
                return diff;
            })
            .limit(k)
            .sorted()
            .collect(Collectors.toList());
    }
}

解释

方法:

这个题解使用了二分查找的思路。我们要在数组中找到一个位置 left,使得区间 [left, left+k) 包含 k 个最接近 x 的元素。由于数组已经按升序排列,所以只需要比较 x 与区间两端点的差值大小即可。如果 x 与左端点的差值更大,说明 left 需要右移;否则说明 left 已经是最优区间的左端点,可以结束查找。

时间复杂度:

O(log n)

空间复杂度:

O(1)

代码细节讲解

🦆
为什么在二分查找的条件中使用`x - arr[mid] > arr[mid + k] - x`来判断是否需要移动左端点?
这个条件用来比较x与区间左端点`arr[mid]`和区间右端点`arr[mid+k]`的距离。如果`x - arr[mid]`大于`arr[mid+k] - x`,意味着x更接近右端点,因此需要将搜索区间向右移动(即移动左端点`left`),以包含更接近x的元素。反之,如果x更接近左端点或与两端点的距离相等,维持当前的左端点位置,因为它可能就是最终的最优区间左端点。
🦆
在执行二分查找时,如果`x`的值非常接近`arr[mid]`或`arr[mid+k]`,这个条件如何确保找到的区间是最优的?
当x非常接近`arr[mid]`或`arr[mid+k]`时,`x - arr[mid]`与`arr[mid + k] - x`的比较仍然有效。如果x几乎等于`arr[mid]`,这意味着左端点可能已经非常接近最优位置,因为x与左端点的距离小于或等于与右端点的距离。此时算法可能会决定维持当前的左端点。如果x接近`arr[mid+k]`,则右端点的距离可能更小,导致算法调整左端点向右,直到找到最接近x的整体区间。
🦆
在二分查找算法中,为什么最终返回的是`arr[left:left+k]`而不是检查`right`边界的值?
在二分查找中,`left`和`right`变量定义了搜索区间的边界。随着算法的进行,`left`和`right`逐渐逼近最优区间的左端点。最终,`left`变量停留在使区间`[left, left+k)`包含最接近x的k个元素的位置。由于我们总是调整`left`或`right`以逼近这一最优位置,所以当`left`停止移动时,它指向的就是这一区间的开始。`right`通常作为搜索的上界,其值不一定是最优区间的一部分。
🦆
考虑到`x`的可能值范围和`arr`的元素范围,是否可能存在`x`远离`arr`所有元素的情况,这时候二分查找的效率如何?
如果`x`远离`arr`的所有元素,二分查找仍然高效。在这种情况下,二分查找将快速收敛到数组的一个端点。例如,如果x远小于`arr`中的最小元素,二分查找将快速定位到数组的最左端;如果x远大于数组中的最大元素,则查找将快速定位到数组的右端(考虑到要取k个元素,实际上是`len(arr)-k`)。在这两种极端情况下,二分查找的步骤数仍然是对数级别的,确保了算法的效率。

相关问题

猜数字大小

猜数字游戏的规则如下:

  • 每轮游戏,我都会从 1 到 n 随机选择一个数字。 请你猜选出的是哪个数字。
  • 如果你猜错了,我会告诉你,你猜测的数字比我选出的数字是大了还是小了。

你可以通过调用一个预先定义好的接口 int guess(int num) 来获取猜测结果,返回值一共有 3 种可能的情况(-11 或 0):

  • -1:我选出的数字比你猜的数字小 pick < num
  • 1:我选出的数字比你猜的数字大 pick > num
  • 0:我选出的数字和你猜的数字一样。恭喜!你猜对了!pick == num

返回我选出的数字。

 

示例 1:

输入:n = 10, pick = 6
输出:6

示例 2:

输入:n = 1, pick = 1
输出:1

示例 3:

输入:n = 2, pick = 1
输出:1

示例 4:

输入:n = 2, pick = 2
输出:2

 

提示:

  • 1 <= n <= 231 - 1
  • 1 <= pick <= n

猜数字大小 II

我们正在玩一个猜数游戏,游戏规则如下:

  1. 我从 1 n 之间选择一个数字。
  2. 你来猜我选了哪个数字。
  3. 如果你猜到正确的数字,就会 赢得游戏
  4. 如果你猜错了,那么我会告诉你,我选的数字比你的 更大或者更小 ,并且你需要继续猜数。
  5. 每当你猜了数字 x 并且猜错了的时候,你需要支付金额为 x 的现金。如果你花光了钱,就会 输掉游戏

给你一个特定的数字 n ,返回能够 确保你获胜 的最小现金数,不管我选择那个数字

 

示例 1:

输入:n = 10
输出:16
解释:制胜策略如下:
- 数字范围是 [1,10] 。你先猜测数字为 7 。
    - 如果这是我选中的数字,你的总费用为 $0 。否则,你需要支付 $7 。
    - 如果我的数字更大,则下一步需要猜测的数字范围是 [8,10] 。你可以猜测数字为 9 。
        - 如果这是我选中的数字,你的总费用为 $7 。否则,你需要支付 $9 。
        - 如果我的数字更大,那么这个数字一定是 10 。你猜测数字为 10 并赢得游戏,总费用为 $7 + $9 = $16 。
        - 如果我的数字更小,那么这个数字一定是 8 。你猜测数字为 8 并赢得游戏,总费用为 $7 + $9 = $16 。
    - 如果我的数字更小,则下一步需要猜测的数字范围是 [1,6] 。你可以猜测数字为 3 。
        - 如果这是我选中的数字,你的总费用为 $7 。否则,你需要支付 $3 。
        - 如果我的数字更大,则下一步需要猜测的数字范围是 [4,6] 。你可以猜测数字为 5 。
            - 如果这是我选中的数字,你的总费用为 $7 + $3 = $10 。否则,你需要支付 $5 。
            - 如果我的数字更大,那么这个数字一定是 6 。你猜测数字为 6 并赢得游戏,总费用为 $7 + $3 + $5 = $15 。
            - 如果我的数字更小,那么这个数字一定是 4 。你猜测数字为 4 并赢得游戏,总费用为 $7 + $3 + $5 = $15 。
        - 如果我的数字更小,则下一步需要猜测的数字范围是 [1,2] 。你可以猜测数字为 1 。
            - 如果这是我选中的数字,你的总费用为 $7 + $3 = $10 。否则,你需要支付 $1 。
            - 如果我的数字更大,那么这个数字一定是 2 。你猜测数字为 2 并赢得游戏,总费用为 $7 + $3 + $1 = $11 。
在最糟糕的情况下,你需要支付 $16 。因此,你只需要 $16 就可以确保自己赢得游戏。

示例 2:

输入:n = 1
输出:0
解释:只有一个可能的数字,所以你可以直接猜 1 并赢得游戏,无需支付任何费用。

示例 3:

输入:n = 2
输出:1
解释:有两个可能的数字 1 和 2 。
- 你可以先猜 1 。
    - 如果这是我选中的数字,你的总费用为 $0 。否则,你需要支付 $1 。
    - 如果我的数字更大,那么这个数字一定是 2 。你猜测数字为 2 并赢得游戏,总费用为 $1 。
最糟糕的情况下,你需要支付 $1 。

 

提示:

  • 1 <= n <= 200

找出第 K 小的数对距离

数对 (a,b) 由整数 ab 组成,其数对距离定义为 ab 的绝对差值。

给你一个整数数组 nums 和一个整数 k ,数对由 nums[i]nums[j] 组成且满足 0 <= i < j < nums.length 。返回 所有数对距离中k 小的数对距离。

 

示例 1:

输入:nums = [1,3,1], k = 1
输出:0
解释:数对和对应的距离如下:
(1,3) -> 2
(1,1) -> 0
(3,1) -> 2
距离第 1 小的数对是 (1,1) ,距离为 0 。

示例 2:

输入:nums = [1,1,1], k = 2
输出:0

示例 3:

输入:nums = [1,6,1], k = 3
输出:5

 

提示:

  • n == nums.length
  • 2 <= n <= 104
  • 0 <= nums[i] <= 106
  • 1 <= k <= n * (n - 1) / 2