返回

树和堆 - 探索强大、高效的数据结构

前端

在数据结构的世界里,树和堆因其强大的组织和存储能力而备受推崇。它们广泛应用于各种领域,例如文件系统、数据库索引、内存管理和搜索算法等。

树结构:枝繁叶茂的存储方式

树形结构是一种具有层次关系的数据结构,类似于我们现实世界中常见的树木结构。它由一个被称为根节点的起始节点,以及零个或多个子节点组成。子节点可以进一步拥有自己的子节点,依此类推,形成一个枝繁叶茂的层次结构。

在树形结构中,父节点和子节点之间存在着父子关系,每个节点都可以拥有多个子节点,但只能有一个父节点。树形结构提供了高效的存储和搜索方式,可以快速地查找、插入和删除节点。

堆结构:快速排序的利器

堆是一种特殊的树形结构,它遵循以下两个基本规则:

  • 完全二叉树规则: 堆必须是一棵完全二叉树,即除了最底层,每个节点都必须拥有两个子节点。
  • 堆序规则: 堆必须满足堆序性质,即每个节点的值都必须大于或等于其子节点的值。

堆结构通常用于实现优先级队列,其中元素按照其优先级存储在堆中。当需要查找或删除具有最高优先级的元素时,堆结构可以高效地完成这些操作。

JavaScript 实现:让树和堆活起来

JavaScript 提供了丰富的数据结构和算法支持,我们可以利用这些工具轻松实现树和堆。以下是一些示例:

// 定义一个二叉树节点类
class Node {
  constructor(data) {
    this.data = data;
    this.left = null;
    this.right = null;
  }
}

// 定义一个二叉树类
class BinaryTree {
  constructor() {
    this.root = null;
  }

  // 插入一个节点
  insert(data) {
    const newNode = new Node(data);
    if (this.root === null) {
      this.root = newNode;
    } else {
      this._insertNode(newNode, this.root);
    }
  }

  // 在二叉树中插入一个节点
  _insertNode(newNode, currentNode) {
    if (newNode.data < currentNode.data) {
      if (currentNode.left === null) {
        currentNode.left = newNode;
      } else {
        this._insertNode(newNode, currentNode.left);
      }
    } else {
      if (currentNode.right === null) {
        currentNode.right = newNode;
      } else {
        this._insertNode(newNode, currentNode.right);
      }
    }
  }

  // 查找一个节点
  find(data) {
    if (this.root === null) {
      return null;
    } else {
      return this._findNode(data, this.root);
    }
  }

  // 在二叉树中查找一个节点
  _findNode(data, currentNode) {
    if (currentNode === null) {
      return null;
    } else if (data === currentNode.data) {
      return currentNode;
    } else if (data < currentNode.data) {
      return this._findNode(data, currentNode.left);
    } else {
      return this._findNode(data, currentNode.right);
    }
  }

  // 删除一个节点
  delete(data) {
    if (this.root === null) {
      return;
    } else {
      this._deleteNode(data, this.root);
    }
  }

  // 在二叉树中删除一个节点
  _deleteNode(data, currentNode) {
    if (currentNode === null) {
      return;
    } else if (data === currentNode.data) {
      this._removeNode(currentNode);
    } else if (data < currentNode.data) {
      this._deleteNode(data, currentNode.left);
    } else {
      this._deleteNode(data, currentNode.right);
    }
  }

  // 删除一个节点
  _removeNode(currentNode) {
    if (currentNode.left === null && currentNode.right === null) {
      currentNode = null;
    } else if (currentNode.left === null) {
      currentNode = currentNode.right;
    } else if (currentNode.right === null) {
      currentNode = currentNode.left;
    } else {
      const successor = this._findSuccessor(currentNode);
      currentNode.data = successor.data;
      this._deleteNode(successor, currentNode);
    }
  }

  // 查找一个节点的后继节点
  _findSuccessor(currentNode) {
    let successor = currentNode.right;
    while (successor.left !== null) {
      successor = successor.left;
    }
    return successor;
  }
}

// 定义一个堆类
class Heap {
  constructor() {
    this.heap = [];
  }

  // 插入一个元素
  insert(data) {
    this.heap.push(data);
    this._heapifyUp();
  }

  // 从堆中删除并返回最大元素
  extractMax() {
    const max = this.heap[0];
    this.heap[0] = this.heap[this.heap.length - 1];
    this.heap.pop();
    this._heapifyDown();
    return max;
  }

  // 上浮操作
  _heapifyUp() {
    let currentIndex = this.heap.length - 1;
    while (currentIndex > 0) {
      const parentIndex = Math.floor((currentIndex - 1) / 2);
      if (this.heap[currentIndex] > this.heap[parentIndex]) {
        [this.heap[currentIndex], this.heap[parentIndex]] = [
          this.heap[parentIndex],
          this.heap[currentIndex],
        ];
        currentIndex = parentIndex;
      } else {
        break;
      }
    }
  }

  // 下沉操作
  _heapifyDown() {
    let currentIndex = 0;
    while (currentIndex < this.heap.length) {
      const leftIndex = 2 * currentIndex + 1;
      const rightIndex = 2 * currentIndex + 2;
      let largestIndex = currentIndex;
      if (
        leftIndex < this.heap.length &&
        this.heap[leftIndex] > this.heap[largestIndex]
      ) {
        largestIndex = leftIndex;
      }
      if (
        rightIndex < this.heap.length &&
        this.heap[rightIndex] > this.heap[largestIndex]
      ) {
        largestIndex = rightIndex;
      }
      if (largestIndex !== currentIndex) {
        [this.heap[currentIndex], this.heap[largestIndex]] = [
          this.heap[largestIndex],
          this.heap[currentIndex],
        ];
        currentIndex = largestIndex;
      } else {
        break;
      }
    }
  }
}

这些仅仅是 JavaScript 实现树和堆的示例,还有许多其他方式可以实现它们。具体实现方式取决于具体需求和场景。

探索更多:树和堆的奥秘

树和堆只是数据结构世界中众多成员中的两个。还有很多其他的数据结构,各有其独特的特性和应用场景。如果您想进一步探索数据结构的奥秘,可以参考以下资源:

  • 《算法导论》
  • 《数据结构与算法》
  • 《JavaScript 数据结构与算法》
  • 《算法可视化工具》

这些资源将帮助您深入理解树、堆和其他数据结构的工作原理,并学会如何有效地使用它们来解决各种现实世界的问题。