js(101)~面试题 17.14. 最小K个数-最小堆-最大堆

119 阅读2分钟

力扣本题传送门

image.png

这个题之前写过,求最小的k个数,这次力求自己写出来,并且写成一个模版,
然后我自己写,我写的思路运行超时,我写的大顶堆,报错真是服了,但是我写的大顶堆最后经过n次实验也是对的啊 我真是服了
又经过n次排查终于找到问题了,
这道题比较经典,明天还写这道题,用快排算法尝试

一下为排查过正确代码\

/**
 * @param {number[]} arr
 * @param {number} k
 * @return {number[]}
 */
function smallestK(arr, k) {

	/* if (arr.length < k) {
		return arr;
	} */

	// const temp = arr.slice(0, k-1);
	const h = new Heap()

	for (let i = 0; i < arr.length; i++) {
		const e = arr[i];
		// 先插入k个数到最小堆中
		if (i < k) {
			h.insert(e)
		} else  if (e < h.container[0]) {
            // 当前元素比 最小栈顶元素(也就是最小栈中最小的值) 还小才堆 
           
				// 保持最小堆一直是k个数 所以 需要先弹出 在入堆
				h.pop();
				h.insert(e)
			}
		
	}
        return h.container;

}


const defaultCmp = (x, y) => x > y;
const swap = (arr, i, j) => [arr[i], arr[j]] = [arr[j], arr[i]];
class Heap1 {
	constructor(cmp = defaultCmp) {
		this.container = [];
		this.cmp = cmp;
	}
	// 从下往上 入堆操作
	insert(data) {
		const { container, cmp } = this;
		container.push(data);
		let index = this.size() - 1;
		while (index) {
			let parent = index - 1 >> 1;
			// 如果当前元素不大于当前元素的根元素 就停止
			if (!cmp(container[index], container[parent])) {
				return;
			}
			// 否则 交换
			swap(container, index, parent);
			// 上移动
			index = parent;
		}


	}

	//  弹出堆顶元素操作
	pop() {
		const { container, cmp } = this;
		if (!this.size()) {
			return null;
		}

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

		// 开始 从上往下比较重新排序 需要堆顶坐标一个变量index,和另外一个标量用来做对比叫做exchange
		let index = 0;
		let exchange = 2 * index + 1; // 先赋值做节点
		let length = this.size();
		// 只要不越界就开始循环
		while (exchange < length) {
			// 判断有右节点不越界 并且右子节点比左节点大 则继续交换
			let right = 2 * index + 2;
			if (right < length && cmp(container[right], container[exchange])) {
				exchange = right;
			}
			// exhchage 不比 index 大 就终止 break
			if (!cmp(container[exchange], container[index])) {
				break;
			}
			// 交换exchange和index
			swap(container, exchange, index);
			// 重置index和exchange 为下一步做对比
			index = exchange;
			exchange = 2 * index + 1;
		}
		// 返回弹出的最大值 即堆顶元素
		return res;
	}

	// 堆的个数
	size() {
		return this.container.length;
	}
	// 堆顶元素
	peek() {
		if (this.size()) {
			return this.container[0];
		}
		return null;
	}
}


class Heap {
	constructor(cmp = defaultCmp) {
		// this.q = [];
		this.container = [];
		this.cmp = cmp;
	}
    // 入堆 从下往上插入 又称 上浮
	insert(v) {
		const { container, cmp } = this;
		container.push(v);

		let i = container.length - 1;
		while (i) {
			let p = (i-1) >> 1;

			if (cmp(container[i], container[p])) {
				swap(container, i, p)

			i = p;
			} else {
                break;
            }

		}
	}
    
    // 弹出堆顶,并返回
    pop1() {
        const { container, cmp } = this;
        let l = container.length;
        swap(container, 0, l - 1);
        const res = container.pop();
        const length = l;
        let index = 0,
            exchange = index * 2 + 1;
        while (exchange < length) {
            // // 以最大堆的情况来说:如果有右节点,并且右节点的值大于左节点的值
            let right = index * 2 + 2;
            // ------ 
            if (right < length && cmp(container[right], container[exchange])) {
                exchange = right;
            }
            if (cmp(container[exchange], container[index])) {
                 swap(container, exchange, index);
            index = exchange;
            exchange = index * 2 + 1;
            } else {
                return;
            }
        }
        return res;
    }
	
	// 出堆 从下往上 又称 下沉
	pop() {
		const { container, cmp } = this;
		let i = 0;
		let l = container.length;
		let e = 2 * i + 1;
		swap(container, i, l - 1);
		container.pop();

		while (e < l) {
			let r = 2 * i + 2;
                        // 这里面cmp 参数穿多了
			if (r < l && cmp( container[r], container[e])) {
				e = r;
			}
			if (cmp( container[e], container[i])) {
				swap(container, i, e);
                i = e;
			    e = 2 * i + 1;
			} else {
                break;
            }
			
		}

	}
}

一下有错误代码又正确代码

/**
 * @param {number[]} arr
 * @param {number} k
 * @return {number[]}
 */
// function smallestK(arr, k) {

// 	/* if (arr.length < k) {
// 		return arr;
// 	} */

// 	// const temp = arr.slice(0, k-1);
// 	const h = new Heap()

// 	for (let i = 0; i < arr.length; i++) {
// 		const e = arr[i];
// 		// 先插入k个数到最小堆中
// 		if (i < k) {
// 			h.insert(e)
// 		} else  if (e < h.container[0]) {
//             // 当前元素比 最小栈顶元素(也就是最小栈中最小的值) 还小才堆 
//             console.log(i,h.container, h.container[0]) 
// 				// 保持最小堆一直是k个数 所以 需要先弹出 在入堆
// 				h.pop();
// 				h.insert(e)
// 			}
		
// 	}
// const ans = [];
//   for (let i = 0; i < k; i++) {
//             ans.unshift(h.pop())
//         }
//         return ans;

// }
const smallestK = (arr, k) => {
    const minHeap = new Heap();
    for (const num of arr) {
        minHeap.insert(num);
    }
    const res = [];
    for (let i = 0; i < k; i++) {
        res.push(minHeap.pop());
    }
    return minHeap.container;
};


const defaultCmp = (x, y) => x < y;
const swap = (arr, i, j) => [arr[i], arr[j]] = [arr[j], arr[i]];
class Heap {
	constructor(cmp = defaultCmp) {
		this.container = [];
		this.cmp = cmp;
	}
	// 从下往上 入堆操作
	insert(data) {
		const { container, cmp } = this;
		container.push(data);
		let index = this.size() - 1;
		while (index) {
			let parent = index - 1 >> 1;
			// 如果当前元素不大于当前元素的根元素 就停止
			if (!cmp(container[index], container[parent])) {
				return;
			}
			// 否则 交换
			swap(container, index, parent);
			// 上移动
			index = parent;
		}


	}

	//  弹出堆顶元素操作
	pop() {
		const { container, cmp } = this;
		if (!this.size()) {
			return null;
		}

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

		// 开始 从上往下比较重新排序 需要堆顶坐标一个变量index,和另外一个标量用来做对比叫做exchange
		let index = 0;
		let exchange = 2 * index + 1; // 先赋值做节点
		let length = this.size();
		// 只要不越界就开始循环
		while (exchange < length) {
			// 判断有右节点不越界 并且右子节点比左节点大 则继续交换
			let right = 2 * index + 2;
			if (right < length && cmp(container[right], container[exchange])) {
				exchange = right;
			}
			// exhchage 不比 index 大 就终止 break
			if (!cmp(container[exchange], container[index])) {
				break;
			}
			// 交换exchange和index
			swap(container, exchange, index);
			// 重置index和exchange 为下一步做对比
			index = exchange;
			exchange = 2 * index + 1;
		}
		// 返回弹出的最大值 即堆顶元素
		return res;
	}

	// 堆的个数
	size() {
		return this.container.length;
	}
	// 堆顶元素
	peek() {
		if (this.size()) {
			return this.container[0];
		}
		return null;
	}
}

// const swap = (arr, i, j) => [arr[i], arr[j]] = [arr[j], arr[i]];
// const defaultCmp = (a, b) => a < b;
// class Heap {
// 	constructor(cmp = defaultCmp) {
// 		this.q = [];
// 		this.cmp = cmp;
// 	}
// 	// 入堆 从下往上插入 又称 上浮
// 	insert(v) {
// 		const { q, cmp } = this;
// 		q.push(v);

// 		let i = q.length - 1;
// 		while (i) {
// 			let p = i-1 >> 1;

// 			if (p && cmp(q[p], cmp[q[i]])) {
// 				swap(q, i, p)

// 			i = p;
// 			} else {
//                 break;
//             }

// 		}
// 	}
// 	// 出堆 从下往上 又称 下沉
// 	pop() {
// 		const { q, cmp } = this;
// 		let i = 0;
// 		let l = q.length;
// 		let e = 2 * i + 1;
// 		swap(q, i, l - 1);
// 		q.pop();

// 		while (e < l) {
// 			let r = 2 * i + 2;
// 			if (r < l && cmp(q, q[r], q[l])) {
// 				e = r;
// 			}
// 			if (cmp(q, q[i], q[e])) {
// 				swap(q, i, e);
//                 i = e;
// 			e = 2 * i + 1;
// 			} else {
//                 break;
//             }
			
// 		}

// 	}
// }