返回
JavaScript 中解决 LeetCode 295 数据流中位数的暴力、二分、堆栈方法
前端
2023-10-01 14:43:10
在数据流中,随着新元素的不断加入,实时求取中位数是一项常见的任务。本文将探讨使用 JavaScript 语言解决 LeetCode 295 数据流中位数的三种方法:暴力法、二分法和堆栈法。
暴力法
暴力法是通过将数据流中的所有元素排序,然后根据元素的数量来计算中位数。对于包含 n 个元素的数据流,暴力法的时间复杂度为 O(n log n),因为它需要对数据流进行排序。
class MedianFinder {
constructor() {
this.arr = [];
}
addNum(num) {
this.arr.push(num);
this.arr.sort((a, b) => a - b);
}
findMedian() {
const n = this.arr.length;
if (n % 2 === 0) {
return (this.arr[n / 2 - 1] + this.arr[n / 2]) / 2;
} else {
return this.arr[Math.floor(n / 2)];
}
}
}
二分法
二分法可以优化暴力法的时间复杂度,将其降低到 O(log n)。它通过使用二分法在排序后的数据流中查找中位数,从而避免了对整个数据流进行排序。
class MedianFinder {
constructor() {
this.arr = [];
}
addNum(num) {
const idx = this.findInsertionPoint(num);
this.arr.splice(idx, 0, num);
}
findInsertionPoint(num) {
let left = 0;
let right = this.arr.length;
while (left < right) {
const mid = Math.floor((left + right) / 2);
if (this.arr[mid] < num) {
left = mid + 1;
} else {
right = mid;
}
}
return left;
}
findMedian() {
const n = this.arr.length;
if (n % 2 === 0) {
return (this.arr[n / 2 - 1] + this.arr[n / 2]) / 2;
} else {
return this.arr[Math.floor(n / 2)];
}
}
}
堆栈法
堆栈法利用最小堆和最大堆来维护数据流的中位数。最小堆存储较大的元素,而最大堆存储较小的元素。中位数可以通过这两个堆的顶元素来计算。使用堆栈法,添加新元素的时间复杂度为 O(log n),而查找中位数的时间复杂度为 O(1)。
class MedianFinder {
constructor() {
this.maxHeap = new MaxHeap();
this.minHeap = new MinHeap();
}
addNum(num) {
if (this.maxHeap.size === 0 || num <= this.maxHeap.peek()) {
this.maxHeap.insert(num);
} else {
this.minHeap.insert(num);
}
this.rebalanceHeaps();
}
rebalanceHeaps() {
while (this.maxHeap.size > this.minHeap.size) {
this.minHeap.insert(this.maxHeap.poll());
}
while (this.minHeap.size > this.maxHeap.size + 1) {
this.maxHeap.insert(this.minHeap.poll());
}
}
findMedian() {
if (this.maxHeap.size === this.minHeap.size) {
return (this.maxHeap.peek() + this.minHeap.peek()) / 2;
} else {
return this.maxHeap.size > this.minHeap.size ? this.maxHeap.peek() : this.minHeap.peek();
}
}
}
class Heap {
constructor() {
this.heap = [];
}
get size() {
return this.heap.length;
}
peek() {
return this.heap[0];
}
insert(num) {
this.heap.push(num);
this.heapifyUp();
}
poll() {
const root = this.heap[0];
this.heap[0] = this.heap.pop();
this.heapifyDown();
return root;
}
heapifyUp() {
let current = this.heap.length - 1;
while (current > 0) {
const parent = Math.floor((current - 1) / 2);
if (this.compare(this.heap[current], this.heap[parent]) > 0) {
this.swap(current, parent);
current = parent;
} else {
break;
}
}
}
heapifyDown() {
let current = 0;
while (current < this.heap.length) {
const left = 2 * current + 1;
const right = 2 * current + 2;
let largest = current;
if (left < this.heap.length && this.compare(this.heap[left], this.heap[current]) > 0) {
largest = left;
}
if (right < this.heap.length && this.compare(this.heap[right], this.heap[largest]) > 0) {
largest = right;
}
if (largest !== current) {
this.swap(current, largest);
current = largest;
} else {
break;
}
}
}
compare(a, b) {
return a - b;
}
swap(a, b) {
const temp = this.heap[a];
this.heap[a] = this.heap[b];
this.heap[b] = temp;
}
}
class MaxHeap extends Heap {
compare(a, b) {
return b - a;
}
}
class MinHeap extends Heap {
compare(a, b) {
return a - b;
}
}
结论
解决 LeetCode 295 数据流中位数问题有三种方法:暴力法、二分法和堆栈法。每种方法都有其优缺点,在不同的场景下可能更适用。暴力法简单易懂,但效率较低。二分法提高了效率,但需要维护排序后的数据流。堆栈法具有最高的效率和最快的查找中位数时间,但实现难度稍高。选择哪种方法取决于具体需求和时间/空间复杂度要求。