返回

JavaScript排序二叉树的奇妙世界:巧妙实现与实战应用

前端

技术博文:《JavaScript中的排序二叉树:实现和应用》

排序二叉树,也称为二叉搜索树,是一种特殊的二叉树,其中每个节点都存储着一个键值,并且该键值小于其左子树的所有键值,大于其右子树的所有键值。这种性质使得排序二叉树在查找、插入和删除操作中具有高效的性能。

在JavaScript中实现排序二叉树,我们可以采用面向对象的编程方式。首先,我们定义一个Node类,该类将表示排序二叉树中的每个节点。Node类包含三个属性:键值(key)、左子节点(left)和右子节点(right)。

class Node {
  constructor(key) {
    this.key = key;
    this.left = null;
    this.right = null;
  }
}

接下来,我们定义一个BinarySearchTree类,该类将实现排序二叉树的基本操作。BinarySearchTree类包含一个根节点(root)属性,该属性指向排序二叉树的根节点。

class BinarySearchTree {
  constructor() {
    this.root = null;
  }

插入

为了在排序二叉树中插入一个新节点,我们需要从根节点开始,并根据键值的大小决定将新节点插入到左子树还是右子树。如果要插入的键值小于当前节点的键值,则将新节点插入到左子树中。否则,将新节点插入到右子树中。

insert(key) {
  const newNode = new Node(key);
  if (this.root === null) {
    this.root = newNode;
  } else {
    this._insert(newNode, this.root);
  }
}

_insert(newNode, currentNode) {
  if (newNode.key < currentNode.key) {
    if (currentNode.left === null) {
      currentNode.left = newNode;
    } else {
      this._insert(newNode, currentNode.left);
    }
  } else {
    if (currentNode.right === null) {
      currentNode.right = newNode;
    } else {
      this._insert(newNode, currentNode.right);
    }
  }
}

查找

为了在排序二叉树中查找一个键值,我们需要从根节点开始,并根据键值的大小决定是向左子树还是向右子树查找。如果要查找的键值等于当前节点的键值,则查找成功,返回该节点。否则,继续在左子树或右子树中查找。

find(key) {
  if (this.root === null) {
    return null;
  } else {
    return this._find(key, this.root);
  }
}

_find(key, currentNode) {
  if (key === currentNode.key) {
    return currentNode;
  } else if (key < currentNode.key) {
    if (currentNode.left === null) {
      return null;
    } else {
      return this._find(key, currentNode.left);
    }
  } else {
    if (currentNode.right === null) {
      return null;
    } else {
      return this._find(key, currentNode.right);
    }
  }
}

删除

删除排序二叉树中的一个节点是一个比较复杂的操作。首先,我们需要找到要删除的节点。然后,根据该节点的情况,决定是直接删除该节点,还是用其左子树或右子树的最小或最大节点替换该节点。

remove(key) {
  if (this.root === null) {
    return;
  } else {
    this._remove(key, this.root);
  }
}

_remove(key, currentNode) {
  if (key === currentNode.key) {
    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 smallestNode = this._findMin(currentNode.right);
      currentNode.key = smallestNode.key;
      this._remove(smallestNode.key, currentNode.right);
    }
  } else if (key < currentNode.key) {
    if (currentNode.left === null) {
      return;
    } else {
      this._remove(key, currentNode.left);
    }
  } else {
    if (currentNode.right === null) {
      return;
    } else {
      this._remove(key, currentNode.right);
    }
  }
}

_findMin(node) {
  while (node.left !== null) {
    node = node.left;
  }
  return node;
}

平衡

为了保持排序二叉树的平衡,我们可以采用各种平衡算法。最常用的平衡算法之一是红黑树算法。红黑树算法将每个节点标记为红色或黑色,并通过一系列规则来确保树的高度保持平衡。

class RedBlackTree {
  constructor() {
    this.root = null;
  }

  // 插入操作
  insert(key) {
    const newNode = new Node(key);
    this._insert(newNode);
    this._fixInsert(newNode);
  }

  _insert(newNode) {
    if (this.root === null) {
      this.root = newNode;
    } else {
      this._insertHelper(newNode, this.root);
    }
  }

  _insertHelper(newNode, currentNode) {
    if (newNode.key < currentNode.key) {
      if (currentNode.left === null) {
        currentNode.left = newNode;
      } else {
        this._insertHelper(newNode, currentNode.left);
      }
    } else {
      if (currentNode.right === null) {
        currentNode.right = newNode;
      } else {
        this._insertHelper(newNode, currentNode.right);
      }
    }
  }

  // 修复插入后的红黑树
  _fixInsert(newNode) {
    while (newNode !== this.root && newNode.parent.color === 'red') {
      if (newNode.parent === newNode.parent.parent.left) {
        const uncle = newNode.parent.parent.right;
        if (uncle.color === 'red') {
          newNode.parent.color = 'black';
          uncle.color = 'black';
          newNode.parent.parent.color = 'red';
          newNode = newNode.parent.parent;
        } else {
          if (newNode === newNode.parent.right) {
            newNode = newNode.parent;
            this._leftRotate(newNode);
          }
          newNode.parent.color = 'black';
          newNode.parent.parent.color = 'red';
          this._rightRotate(newNode.parent.parent);
        }
      } else {
        const uncle = newNode.parent.parent.left;
        if (uncle.color === 'red') {
          newNode.parent.color = 'black';
          uncle.color = 'black';
          newNode.parent.parent.color = 'red';
          newNode = newNode.parent.parent;
        } else {
          if (newNode === newNode.parent.left) {
            newNode = newNode.parent;
            this._rightRotate(newNode);
          }
          newNode.parent.color = 'black';
          newNode.parent.parent.color = 'red';
          this._leftRotate(newNode.parent.parent);
        }
      }
    }
    this.root.color = 'black';
  }

  // 左旋操作
  _leftRotate(node) {
    const rightChild = node.right;
    node.right = rightChild.left;
    if (rightChild.left !== null) {
      rightChild.left.parent = node;
    }
    rightChild.parent = node.parent;
    if (node.parent === null) {
      this.root = rightChild;
    } else if