📌  相关文章
📜  day 22 二叉搜索树hackerrank解决方案javascript(1)

📅  最后修改于: 2023-12-03 15:14:37.603000             🧑  作者: Mango

Day 22 - 二叉搜索树

二叉搜索树是一种经典的数据结构,它有着很多的应用场景。在本章中,我们将学习如何使用 JavaScript 来实现一个二叉搜索树,并在 Hackerrank 平台上完成一道关于二叉搜索树的练习题。

什么是二叉搜索树?

二叉搜索树(Binary Search Tree,简称 BST)是一种有序树,它将所有的值存储在树的节点中,并且满足以下性质:

  1. 对于任意一个节点,它的左子树中所有的节点的值都小于它的值;
  2. 对于任意一个节点,它的右子树中所有的节点的值都大于它的值;
  3. 左子树和右子树都是二叉搜索树。

下面是一个例子:

      4
     / \
    2   6
   / \ / \
  1  3 5  7

这个二叉搜索树有以下特点:

  1. 根节点的值为 4;
  2. 左子树中所有节点的值都小于 4,右子树中所有节点的值都大于 4;
  3. 左子树是一个二叉搜索树,右子树也是一个二叉搜索树。
如何实现二叉搜索树?
节点的定义

首先,我们需要定义二叉搜索树中的节点。每个节点包含三个属性:值、左子节点和右子节点。

class Node {
  constructor(value) {
    this.value = value;
    this.left = null;
    this.right = null;
  }
}
插入节点

接下来,我们需要实现插入节点的方法,它用来向二叉搜索树中添加新的节点。

class BinaryTree {
  constructor() {
    this.root = null;
  }
  
  insert(value) {
    const newNode = new Node(value);
    
    if (!this.root) {
      this.root = newNode;
      return;
    }
    
    let current = this.root;
    while (true) {
      if (value < current.value) {
        if (!current.left) {
          current.left = newNode;
          break;
        }
        current = current.left;
      } else if (value > current.value) {
        if (!current.right) {
          current.right = newNode;
          break;
        }
        current = current.right;
      } else {
        // 如果值已存在,则不执行任何操作
        break;
      }
    }
  }
}

在这个方法中,首先创建一个新的节点。如果树为空,那么将节点设置为根节点并返回;否则,从根节点开始遍历树,直到找到一个合适的位置。

如果要插入的值小于当前节点的值,则向左子树移动;如果要插入的值大于当前节点的值,则向右子树移动。如果要插入的值已经存在于树中,则不执行任何操作。

查找节点

查找节点是二叉搜索树的一个重要操作。以下是查找节点的方法:

class BinaryTree {
  // ...
  
  find(value) {
    let current = this.root;
    while (current) {
      if (value === current.value) {
        return current;
      } else if (value < current.value) {
        current = current.left;
      } else if (value > current.value) {
        current = current.right;
      }
    }
    return null;
  }
}

在这个方法中,从根节点开始遍历树,如果要查找的值等于当前节点的值,则返回当前节点。如果要查找的值小于当前节点的值,则向左子树移动;如果要查找的值大于当前节点的值,则向右子树移动。如果找不到包含要查找的值的节点,则返回 null。

删除节点

删除节点是编写二叉搜索树代码的最难部分之一。以下是删除节点的方法:

class BinaryTree {
  // ...
  
  delete(value) {
    this.root = this._deleteNode(this.root, value);
  }
  
  _deleteNode(current, value) {
    if (!current) {
      return null;
    }
    
    if (value === current.value) {
      if (!current.left && !current.right) {
        return null;
      } else if (!current.left) {
        return current.right;
      } else if (!current.right) {
        return current.left;
      } else {
        // 左右子树都存在,需要找到左子树中最大的节点或右子树中最小的节点
        let maxLeft = current.left;
        while (maxLeft.right) {
          maxLeft = maxLeft.right;
        }
        current.value = maxLeft.value;
        current.left = this._deleteNode(current.left, maxLeft.value);
      }
    } else if (value < current.value) {
      current.left = this._deleteNode(current.left, value);
    } else if (value > current.value) {
      current.right = this._deleteNode(current.right, value);
    }
    
    return current;
  }
}

在这个方法中,我们使用递归来删除节点。首先检查当前节点是否存在。如果要删除的值等于当前节点的值,则有以下几种情况:

  1. 如果当前节点是叶子节点(即没有左子节点和右子节点),则直接返回 null。
  2. 如果当前节点只有一个子节点,那么将其子节点替换掉当前节点并返回;
  3. 如果当前节点有两个子节点,那么需要找到左子树中最大的节点或右子树中最小的节点来替换当前节点。在这个例子中,我们选择从左子树中找到最大的节点。

如果要删除的值小于当前节点的值,则向左子树移动;如果要删除的值大于当前节点的值,则向右子树移动。

后序遍历

在接下来的练习中,我们需要实现后序遍历二叉搜索树。以下是后序遍历的代码:

class BinaryTree {
  // ...
  
  _postorderTraversal(node, output) {
    if (!node) {
      return;
    }
    
    this._postorderTraversal(node.left, output);
    this._postorderTraversal(node.right, output);
    output.push(node.value);
  }
  
  postorderTraversal() {
    const output = [];
    this._postorderTraversal(this.root, output);
    return output;
  }
}

在这个方法中,我们使用递归来后序遍历二叉搜索树。首先遍历左子树,然后遍历右子树,最后将当前节点的值加入到输出数组中。

Hackerrank 练习题

在 Hackerrank 上,我们需要完成一个关于二叉搜索树的一个练习题。在这个练习中,我们需要输入一个数组,然后将其插入到一个二叉搜索树中。最后,我们需要后序遍历这个二叉搜索树,并将遍历结果输出。

以下是完整的解决方案:

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

class BinaryTree {
  constructor() {
    this.root = null;
  }
  
  insert(value) {
    const newNode = new Node(value);
    
    if (!this.root) {
      this.root = newNode;
      return;
    }
    
    let current = this.root;
    while (true) {
      if (value < current.value) {
        if (!current.left) {
          current.left = newNode;
          break;
        }
        current = current.left;
      } else if (value > current.value) {
        if (!current.right) {
          current.right = newNode;
          break;
        }
        current = current.right;
      } else {
        break;
      }
    }
  }
  
  _postorderTraversal(node, output) {
    if (!node) {
      return;
    }
    
    this._postorderTraversal(node.left, output);
    this._postorderTraversal(node.right, output);
    output.push(node.value);
  }
  
  postorderTraversal() {
    const output = [];
    this._postorderTraversal(this.root, output);
    return output;
  }
}

function processData(input) {
    // 解析输入
    const lines = input.trim().split('\n');
    const n = parseInt(lines[0]);
    const a = lines[1].split(' ').map(x => parseInt(x));
    
    // 创建二叉搜索树并插入元素
    const bst = new BinaryTree();
    for (let i = 0; i < n; i++) {
        bst.insert(a[i]);
    }
    
    // 后序遍历并输出结果
    const result = bst.postorderTraversal();
    console.log(result.join(' '));
} 

在这个解决方案中,我们首先输入一个数组并将其解析为一个整数数组。然后,我们创建一个新的二叉搜索树并将每个元素插入树中。最后,我们实现了一个后序遍历的函数来遍历树并将值添加到输出数组中。最后,我们将输出数组的元素连接成一个字符串,并将其输出。

总结

在本章中,我们学习了二叉搜索树的定义、插入、查找、删除和遍历节点。我们还提供了一个完整的解决方案,用于在 Hackerrank 平台上完成关于二叉搜索树的一个练习题。