js(82)~973. 最接近原点的 K 个点

82 阅读4分钟

力扣本题传送门

这种类似的,求最什么的几个值,都是可以用大顶钝小顶堆来解决,也可以用快排思想,最近我在死磕大顶堆,小顶堆,所以首先尝试这种方法.看了题解,只有一个人用堆的方法写的,但是我看的费劲儿,就把他改造了
改造前代码

var kClosest = function(points, K, p = new PriorityQueue()) {
    for (var i = 0, v; i < points.length; i++) {
        v = points[i][0] * points[i][0] + points[i][1] * points[i][1] 
        if (i < K) {
            p.add(v, i) // 0 到 K - 1 项,放入 优先队列
        } else if (v < p.first()) {// < 优先队列第一项
            p.shift() // 优先队列中距离最大的节点弹出
            p.add(v, i) // 放入当前节点,上浮
        }
    }
    return p.q.map(v => points[v.second]) // 将优先队列 还原为 位置
};
class PriorityQueue {
    constructor(a) {
        this.q = []
        a && this._build(a)
    }
    add(v, second) { // 添加
        this.q.push({v, second}) // 添加 值(比较用) 和 第二参数(索引)
        this._up(this.q.length - 1) // 放入 二叉树的尾部,然后 上浮
    }
    shift() { // 弹出
        this.q.shift() // 弹出 根节点
        if (this.q.length) { // 如果还有节点
            this.q.unshift(this.q.pop()) // 将 最尾部的节点 放到根节点位置
            this._down(0) // 下沉 根节点
        }
    }
    first() { // 根节点的值
        return this.q[0].v 
    }
    _build(a) { // 初始化
        this.q.push({v:a[0]}) // 先给空队列放入第0个元素
        for (var i = 1; i < a.length; i++) this.q.unshift({v:a[i]}), this._down(0)
        // 从第1个元素起,把新元素放在根节点,然后下沉 根节点
    }
    _swap(l, r, t) { // 交换
        t = this.q[l], this.q[l] = this.q[r], this.q[r] = t // 交换两个节点
    }
    _down(i){ // 下沉
        var t = this.q.length - 2 >> 1, max, maxI // 叶子节点的根节点索引,下沉到 叶子节点的根节点停止
        while(i <= t){
            var l = i * 2 + 1, r = l + 1 // 左子节点的索引 = 当前节点索引 * 2 + 1,右子节点的索引 = 左子节点的索引 + 1
            if ((this.q[l] ? this.q[l].v : -Infinity) > (this.q[r] ? this.q[r].v : -Infinity))
                 max = this.q[l].v, maxI = l
            else max = this.q[r].v, maxI = r // 找到 左子节点 和 右子节点的 较大者
            if (this.q[i].v < max) this._swap(i, maxI), i = maxI // 当前节点的 左子节点 或 右子节点 比 它大,交换 
            else break
        }
    }
    _up(i) { // 上浮
        while(i > 0){ // 不能超过根节点
            var t = i - 1 >> 1 // 当前节点的 根节点索引 = 当前节点索引 - 1 的一半
            if (this.q[i].v > this.q[t].v) this._swap(i, t), i = t // 当前节点值 比 它的根节点 大,交换
            else break
        }
    }
}

改在后代码, shift 么有办法按照常规思路来考虑 还是等以后功力精进了再来看吧\

/*
 * @lc app=leetcode.cn id=973 lang=javascript
 *
 * [973] 最接近原点的 K 个点
 */

// @lc code=start
var kClosest = function (points, K) {
	const p = new PriorityQueue()
	for (var i = 0, v; i < points.length; i++) {
		v = points[i][0] * points[i][0] + points[i][1] * points[i][1]
		if (i < K) {
			p.add(v, i) // 0 到 K - 1 项,放入 优先队列
		} else if (v < p.first()) {// < 优先队列第一项
			p.shift() // 优先队列中距离最大的节点弹出
			p.add(v, i) // 放入当前节点,上浮
		}
	}
	return p.q.map(v => points[v.second]) // 将优先队列 还原为 位置
};
class PriorityQueue {
	constructor() {
		this.q = []
	}
	add(v, second) { // 添加
		this.q.push({ v, second }) // 添加 值(比较用) 和 第二参数(索引)
		let i = this.q.length - 1;
		while (i > 0) { // 不能超过根节点
			var t = i - 1 >> 1 // 当前节点的 根节点索引 = 当前节点索引 - 1 的一半
			if (this.q[i].v > this.q[t].v) this._swap(i, t), i = t // 当前节点值 比 它的根节点 大,交换
			else break
		}

	}

	shift() { // 弹出
		this.q.shift() // 弹出 根节点
		if (this.q.length) { // 如果还有节点
			this.q.unshift(this.q.pop()) // 将 最尾部的节点 放到根节点位置
			// 叶子节点的根节点索引,下沉到 叶子节点的根节点停止
			// this.q.length - 2 >> 1 
			// 相当于 
			/* 			// 先求出 尾部元素
						const child = this.q.length - 1;
						// 在找到尾元素的跟元素
						let t = child - 1 >> 1 // 这一步相当于 Math.floor((child - 1) /2) */

			let t = this.q.length - 2 >> 1, max, maxI
			let i = 0;
			// 这个while 循环的终止条件还不能 是 i < this.q.length 
			// 里面的判断 太复杂了 
			while (i <= t) {
				let l = i * 2 + 1, r = l + 1 // 左子节点的索引 = 当前节点索引 * 2 + 1,右子节点的索引 = 左子节点的索引 + 1
				if ((this.q[l] ? this.q[l].v : -Infinity) > (this.q[r] ? this.q[r].v : -Infinity)) {
					max = this.q[l].v, maxI = l
				} else { max = this.q[r].v, maxI = r } // 找到 左子节点 和 右子节点的 较大者
				if (this.q[i].v < max) this._swap(i, maxI), i = maxI // 当前节点的 左子节点 或 右子节点 比 它大,交换 
				else break
			}
		}
	}
	/* shift() {
		const { q } = this;
		let length = q.length;
		if (!length) {
			return;
		}


		// 先交换首位节点 
		this._swap(0, length - 1);
		// 交换完以后 弹出最后一个 即是刚刚交换道末尾的 堆顶元素
		const res = q.pop();

		// 开始 从上往下比较重新排序 需要堆顶坐标一个变量index,和另外一个标量用来做对比叫做exchange
		let index = 0;
		let exchange = 2 * index + 1; // 先赋值做节点

		// 只要不越界就开始循环
		while (exchange < length) {
			// 判断有右节点不越界 并且右子节点比左节点大 则继续交换
			let right = 2 * index + 2;
			if (right < length && q[right] > q[exchange]) {
				exchange = right;
			}
			// exhchage 不比 index 大 就终止 break
			if (q[exchange] > q[index]) {
				break;
			}
			// 交换exchange和index
			this._swap(exchange, index);
			// 重置index和exchange 为下一步做对比
			index = exchange;
			exchange = 2 * index + 1;
		}
		// 返回弹出的最大值 即堆顶元素
		return res;

	} */
	first() { // 根节点的值
		return this.q[0].v
	}

	_swap(l, r, t) { // 交换
		t = this.q[l], this.q[l] = this.q[r], this.q[r] = t // 交换两个节点
	}


}



// @lc code=end



以下是错误代码, 发现别人写的代码,有可借鉴也有不容易理解的地方,于是我老是想着改造别人的代码,想把两个人的代码,容易理解的糅合在一块儿,但是基本功还没有联系扎实,总是烤蒙...浪费时间

/*
 * @lc app=leetcode.cn id=973 lang=javascript
 *
 * [973] 最接近原点的 K 个点
 */

// @lc code=start

/**
 * @param {number[]} nums
 * @param {number} k
 * @return {number[]}
 */



function PriorityQueue(compareFn) {
	this.compareFn = compareFn;
	this.queue = [];
}

// 添加
PriorityQueue.prototype.push = function (item) {
	this.queue.push(item);
	let index = this.queue.length - 1;
	let parent = Math.floor((index - 1) / 2);
	// 上浮
	while (parent >= 0 && this.compare(parent, index) > 0) {
		// 交换
		[this.queue[index], this.queue[parent]] = [this.queue[parent], this.queue[index]];
		index = parent;
		parent = Math.floor((index - 1) / 2);
	}
}

// 获取堆顶元素并移除
PriorityQueue.prototype.pop = function () {
	const ret = this.queue[0];

	// 把最后一个节点移到堆顶
	this.queue[0] = this.queue.pop();

	let index = 0;
	// 左子节点下标,left + 1 就是右子节点下标
	let left = 1;
	let selectedChild = this.compare(left, left + 1) > 0 ? left + 1 : left;

	// 下沉
	while (selectedChild !== undefined && this.compare(index, selectedChild) > 0) {
		// 交换
		[this.queue[index], this.queue[selectedChild]] = [this.queue[selectedChild], this.queue[index]];
		index = selectedChild;
		left = 2 * index + 1;
		selectedChild = this.compare(left, left + 1) > 0 ? left + 1 : left;
	}

	return ret;
}

PriorityQueue.prototype.size = function () {
	return this.queue.length;
}

// 使用传入的 compareFn 比较两个位置的元素
PriorityQueue.prototype.compare = function (index1, index2) {
	if (this.queue[index1] === undefined) {
		return 1;
	}
	if (this.queue[index2] === undefined) {
		return -1;
	}

	return this.compareFn(this.queue[index1], this.queue[index2]);
}


/**
* @param {number[][]} points
* @param {number} k
* @return {number[][]}
*/
var kClosest = function (points, K) {
	const p = new PriorityQueue((a, b) => b[1] - a[1]);
	// entry 是一个长度为2的数组,0位置存储key,1位置存储value
	/* 	for (const entry of map.entries()) {
			priorityQueue.push(entry);
			if (priorityQueue.size() > k) {
				priorityQueue.pop();
			}
		} */
	for (var i = 0, v; i < points.length; i++) {
		v = points[i][0] * points[i][0] + points[i][1] * points[i][1]
		p.push([v, i]) // 0 到 K - 1 项,放入 优先队列
		if (p.size() > K) {
			p.pop();
		}
	}
	/* for (var i = 0, v; i < points.length; i++) {
		v = points[i][0] * points[i][0] + points[i][1] * points[i][1] 
		if (i < K) {
				p.add(v, i) // 0 到 K - 1 项,放入 优先队列
		} else if (v < p.first()) {// < 优先队列第一项
				p.shift() // 优先队列中距离最大的节点弹出
				p.add(v, i) // 放入当前节点,上浮
		}
} */


	console.log(p, p.queue);
	return p.queue.map(v => points[v[1]]) // 将优先队列 还原为 位置
};


// @lc code=end