返回

JavaScript 堆数据结构与操作指南

前端

摘要

JavaScript 中的堆数据结构以其快速的操作速度和广泛的应用场景而备受关注。本文将深入探究堆的原理、性质以及 JavaScript 中的堆实现,并通过实际案例演示其在各种场景中的应用。

正文

一、堆数据结构概述

堆是一种特殊的完全二叉树,其每个节点都满足堆的性质:

  • 最大堆:每个节点的值都大于或等于其子节点的值。
  • 最小堆:每个节点的值都小于或等于其子节点的值。

堆通常用数组表示,其中数组中的每个元素都对应于二叉树中的一个节点。

二、JavaScript 中的堆实现

1. 定义堆

class Heap {
  constructor(arr, comparator) {
    this.heap = arr ? [...arr] : [];
    this.comparator = comparator || ((a, b) => a - b);
    this.buildHeap();
  }

  buildHeap() {
    for (let i = Math.floor(this.heap.length / 2) - 1; i >= 0; i--) {
      this.heapify(i);
    }
  }

  heapify(i) {
    const left = 2 * i + 1;
    const right = 2 * i + 2;
    let largest = i;
    if (left < this.heap.length && this.comparator(this.heap[left], this.heap[largest]) > 0) {
      largest = left;
    }
    if (right < this.heap.length && this.comparator(this.heap[right], this.heap[largest]) > 0) {
      largest = right;
    }
    if (largest !== i) {
      [this.heap[i], this.heap[largest]] = [this.heap[largest], this.heap[i]];
      this.heapify(largest);
    }
  }

  insert(value) {
    this.heap.push(value);
    this.heapifyUp(this.heap.length - 1);
  }

  heapifyUp(i) {
    let parent = Math.floor((i - 1) / 2);
    while (i > 0 && this.comparator(this.heap[i], this.heap[parent]) > 0) {
      [this.heap[i], this.heap[parent]] = [this.heap[parent], this.heap[i]];
      i = parent;
      parent = Math.floor((i - 1) / 2);
    }
  }

  remove() {
    if (this.heap.length === 0) {
      return null;
    }
    const value = this.heap[0];
    this.heap[0] = this.heap.pop();
    this.heapifyDown(0);
    return value;
  }

  heapifyDown(i) {
    const left = 2 * i + 1;
    const right = 2 * i + 2;
    let smallest = i;
    if (left < this.heap.length && this.comparator(this.heap[left], this.heap[smallest]) < 0) {
      smallest = left;
    }
    if (right < this.heap.length && this.comparator(this.heap[right], this.heap[smallest]) < 0) {
      smallest = right;
    }
    if (smallest !== i) {
      [this.heap[i], this.heap[smallest]] = [this.heap[smallest], this.heap[i]];
      this.heapifyDown(smallest);
    }
  }

  peek() {
    return this.heap[0];
  }

  size() {
    return this.heap.length;
  }

  isEmpty() {
    return this.heap.length === 0;
  }
}

三、堆的应用

1. 堆排序

堆排序是一种基于堆数据结构的排序算法,其时间复杂度为 O(n log n)。

function heapSort(arr) {
  const heap = new Heap(arr, (a, b) => b - a);
  const sortedArr = [];
  while (!heap.isEmpty()) {
    sortedArr.push(heap.remove());
  }
  return sortedArr;
}

2. 优先队列

优先队列是一种数据结构,其中元素按照优先级排序,优先级最高的元素排在最前面。堆可以很容易地实现优先队列,因为堆的根节点始终是优先级最高的元素。

class PriorityQueue {
  constructor(comparator) {
    this.heap = new Heap([], comparator);
  }

  enqueue(value) {
    this.heap.insert(value);
  }

  dequeue() {
    return this.heap.remove();
  }

  peek() {
    return this.heap.peek();
  }

  size() {
    return this.heap.size();
  }

  isEmpty() {
    return this.heap.isEmpty();
  }
}

总结

堆数据结构在 JavaScript 中有着广泛的应用,其快速的操作速度和简单的实现方式使其成为许多场景的理想选择。通过本文的讲解,您已经对堆的数据结构和实现有了深入的了解,可以将其应用到自己的项目中。