[路飞]_973.最接近原点的K个点

762 阅读1分钟

「这是我参与2022首次更文挑战的第1天,活动详情查看:2022首次更文挑战

题目地址

我们有一个由平面上的点组成的列表 points。需要从中找出 K 个距离原点 (0, 0) 最近的点。

(这里,平面上两点之间的距离是欧几里德距离。)

你可以按任何顺序返回答案。除了点坐标的顺序之外,答案确保是唯一的。

「示例1:」
输入:points = [[1,3],[-2,2]], K = 1
输出:[[-2,2]]解释: (1, 3) 和原点之间的距离为 sqrt(10),
      (-2, 2) 和原点之间的距离为 sqrt(8),
      由于 sqrt(8) < sqrt(10),(-2, 2) 离原点更近。
      我们只需要距离原点最近的 K = 1 个点,
      所以答案就是 [[-2,2]]
「示例2:」
输入:points = [[3,3],[5,-1],[-2,4]], K = 2
输出:[[3,3],[-2,4]](答案 [[-2,4],[3,3]] 也会被接受。)
「提示:」
  1. 1 <= K <= points.length <= 10000

  2. -10000 < points[i][0] < 10000

  3. -10000 < points[i][1] < 10000

解题思路

  从题目「示例1」的解释中我们可以得出距原点之间的距离是当前坐标的两个值的平方和,比如(1, 3)坐标距离原点的距离就是1 * 1 + 3 * 3 = 10所以这个坐标距离原点的距离为10,题目要求我们求出距离原点最近的K个点,由上面的公司我们可以把每一个坐标都想成一个具体的值,然后求出距离原点最近的k个点,就变成了求当前数字里面前k个最小值,题目对答案的顺序不做要求,所以我们就可以用大顶堆来求最小的K个值

代码实现

class BigHeap { // 用class模拟一个大顶堆
    constructor(k) {
        this.arr = []; // 初始化数组
        this.size = 0; // 初始化大小为0
        this.max = k; // 堆的最大值
    }

    push(val) { // 插入操作
        this.arr.push(val);
        this.size++;
        if (this.size > 1) {
          	// cur -> 当前值的下标 parent -> 父节点的下标
            let cur = this.size - 1, parent = cur - 1 >> 1;
            // 如果新插入的值大于堆顶的值,堆就失衡,握紧就要对这个堆进行平衡调整
            while (cur > 0 && compare(this.arr[cur], this.arr[parent])) { 
              	// 平衡调整,交换两个值的位置
                [this.arr[cur], this.arr[parent]] = [this.arr[parent], this.arr[cur]];
                cur = parent;
                parent = cur - 1 >> 1;
            }
        }
      	// 如果堆的大小超过了最大值,就要删除一个值
        if (this.size > this.max) this.pop();
    }

    pop() { // 堆的弹出操作
        this.arr[0] = this.arr.pop();
        this.size--;
      	// 父节点 左子节点 右子节点 的下标
        let cur = 0, childl = 1, childr = 2;
      	// 同样是,如果堆失衡,就进行堆平衡
        while (
            childl < this.size && compare(this.arr[childl], this.arr[cur]) ||
            childr < this.size && compare(this.arr[childr], this.arr[cur])
        ) {
          	// 判断一下两个子节点谁的值更大,就用更大的值和父节点的值进行交换
            if (childr < this.size && compare(this.arr[childr], this.arr[childl])) {
                [this.arr[cur], this.arr[childr]] = [this.arr[childr], this.arr[cur]];
                cur = childr;
            } else {
                [this.arr[cur], this.arr[childl]] = [this.arr[childl], this.arr[cur]];
                cur = childl;
            }
            childl = cur * 2 + 1;
            childr = cur * 2 + 2;
        }
    }

    top() {	// 获取堆顶的值
        return this.arr[0];
    }
}

function compare(arr1, arr2) { // 对每一个坐标点的大小进行比较
    let val1 = arr1[0] * arr1[0] + arr1[1] * arr1[1];
    let val2 = arr2[0] * arr2[0] + arr2[1] * arr2[1];
    return val1 > val2;
}

var kClosest = function(points, k) {
  	// 构建一个堆
    let heap = new BigHeap(k);

  	// 遍历每一个坐标
    for (let i=0; i<points.length; i++) {
      	// 如果当前堆没有超过最大保存数量,或者堆顶的值要大于当前遍历的值,就把当前值放进堆中
        if (heap.size < k || compare(heap.top(), points[i])) {
            heap.push(points[i]);
        }
    }

    // console.log(heap.arr);
    return heap.arr;
};

如果你对这道题目还有疑问的话,可以在评论区进行留言;