class MaxHeap {
private heap: number[];
constructor() {
this.heap = [];
}
// 插入元素并上浮调整
push(num: number): void {
this.heap.push(num);
this.siftUp(this.heap.length - 1);
}
// 弹出堆顶元素并下沉调整
pop(): number {
const top = this.heap[0];
const last = this.heap.pop()!;
if (this.heap.length > 0) {
this.heap[0] = last;
this.siftDown(0);
}
return top;
}
// 堆顶元素
peek(): number {
return this.heap[0];
}
size(): number {
return this.heap.length;
}
// 上浮操作
private siftUp(index: number): void {
while (index > 0) {
const parent = Math.floor((index - 1) / 2);
if (this.heap[parent] >= this.heap[index]) break;
[this.heap[parent], this.heap[index]] = [
this.heap[index],
this.heap[parent],
];
index = parent;
}
}
// 下沉操作
private siftDown(index: number): void {
const size = this.size();
while (index < size) {
let left = 2 * index + 1;
let right = 2 * index + 2;
let largest = index;
if (left < size && this.heap[left] > this.heap[largest]) largest = left;
if (right < size && this.heap[right] > this.heap[largest])
largest = right;
if (largest === index) break;
[this.heap[index], this.heap[largest]] = [
this.heap[largest],
this.heap[index],
];
index = largest;
}
}
}
class MinHeap {
private heap: number[];
constructor() {
this.heap = [];
}
push(num: number): void {
this.heap.push(num);
this.siftUp(this.heap.length - 1);
}
pop(): number {
const top = this.heap[0];
const last = this.heap.pop()!;
if (this.heap.length > 0) {
this.heap[0] = last;
this.siftDown(0);
}
return top;
}
peek(): number {
return this.heap[0];
}
size(): number {
return this.heap.length;
}
private siftUp(index: number): void {
while (index > 0) {
const parent = Math.floor((index - 1) / 2);
if (this.heap[parent] <= this.heap[index]) break;
[this.heap[parent], this.heap[index]] = [
this.heap[index],
this.heap[parent],
];
index = parent;
}
}
private siftDown(index: number): void {
const size = this.size();
while (index < size) {
let left = 2 * index + 1;
let right = 2 * index + 2;
let smallest = index;
if (left < size && this.heap[left] < this.heap[smallest]) smallest = left;
if (right < size && this.heap[right] < this.heap[smallest])
smallest = right;
if (smallest === index) break;
[this.heap[index], this.heap[smallest]] = [
this.heap[smallest],
this.heap[index],
];
index = smallest;
}
}
}
class MedianFinder {
private maxHeap: MaxHeap; // 较小的一半(最大堆)
private minHeap: MinHeap; // 较大的一半(最小堆)
constructor() {
this.maxHeap = new MaxHeap();
this.minHeap = new MinHeap();
}
addNum(num: number): void {
// 优先插入最大堆
this.maxHeap.push(num);
// 将最大堆的堆顶移动到最小堆(确保最大堆堆顶 ≤ 最小堆堆顶)
this.minHeap.push(this.maxHeap.pop());
// 平衡堆大小,确保最大堆大小 >= 最小堆
if (this.maxHeap.size() < this.minHeap.size()) {
this.maxHeap.push(this.minHeap.pop());
}
}
findMedian(): number {
if (this.maxHeap.size() === this.minHeap.size()) {
return (this.maxHeap.peek() + this.minHeap.peek()) / 2;
} else {
return this.maxHeap.peek();
}
}
}
//分块加载
function calculateExactMedian(data: number[], chunkSize: number) {
const chunks = [];
// 分块加载数据
for (let i = 0; i < data.length; i += chunkSize) {
const chunk = data.slice(i, i + chunkSize);
chunks.push(chunk);
}
// 对每个小块进行排序
const sortedChunks = chunks.map((chunk) => chunk.sort((a, b) => a - b));
// 合并所有排序后的块
const mergedArray = [];
sortedChunks.forEach((chunk) => {
mergedArray.push(...chunk);
});
// 对合并后的数组进行排序
mergedArray.sort((a, b) => a - b);
// 计算中位数
const length = mergedArray.length;
if (length % 2 === 0) {
return (mergedArray[length / 2 - 1] + mergedArray[length / 2]) / 2;
} else {
return mergedArray[Math.floor(length / 2)];
}
}
//测试中位数计算效率
const nums: number[] = [];
const len = 10 ** 7;
for (let i = 0; i < len; i++) {
nums.push(Math.floor(Math.random() * 100));
}
const $s = performance.now();
const medianFinder = new MedianFinder();
for (let i = 0; i < nums.length; i++) {
medianFinder.addNum(nums[i]);
}
console.log(medianFinder.findMedian()); // 输出中位数
const $e = performance.now();
console.log($e - $s, '@heap_sort');
//使用普通数组的方式
const $s1 = performance.now();
nums.sort((a, b) => a - b); // 排序
const n = nums.length;
if (n % 2 === 0) {
console.log((nums[n / 2 - 1] + nums[n / 2]) / 2); // 偶数个元素,取中间两个的平均值
} else {
console.log(nums[Math.floor(n / 2)]); // 奇数个元素,取中间的元素}
}
const $e1 = performance.now();
console.log($e1 - $s1, '@arr_sort');
// 设置块大小
const $s2 = performance.now();
const chunkSize = 10000;
console.log(calculateExactMedian(nums, chunkSize));
const $e2 = performance.now();
console.log($e2 - $s2, '@chunk_sort');
leetcode-295 Find Median from Data Stream
于 2025-05-26 16:41:29 首次发布