「前端刷题」173.二叉搜索树迭代器(MEDIUM)

71 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第18天,点击查看活动详情

题目(Binary Search Tree Iterator)

链接:https://leetcode-cn.com/problems/binary-search-tree-iterator
解决数:778
通过率:81.1%
标签:栈 树 设计 二叉搜索树 二叉树 迭代器 
相关公司:facebook amazon microsoft 

实现一个二叉搜索树迭代器类BSTIterator ,表示一个按中序遍历二叉搜索树(BST)的迭代器:

  • BSTIterator(TreeNode root) 初始化 BSTIterator 类的一个对象。BST 的根节点 root 会作为构造函数的一部分给出。指针应初始化为一个不存在于 BST 中的数字,且该数字小于 BST 中的任何元素。
  • boolean hasNext() 如果向指针右侧遍历存在数字,则返回 true ;否则返回 false 。
  • int next()将指针向右移动,然后返回指针处的数字。

注意,指针初始化为一个不存在于 BST 中的数字,所以对 next() 的首次调用将返回 BST 中的最小元素。

你可以假设 next() 调用总是有效的,也就是说,当调用 next() 时,BST 的中序遍历中至少存在一个下一个数字。

 

示例:

输入
["BSTIterator", "next", "next", "hasNext", "next", "hasNext", "next", "hasNext", "next", "hasNext"]
[[[7, 3, 15, null, null, 9, 20]], [], [], [], [], [], [], [], [], []]
输出
[null, 3, 7, true, 9, true, 15, true, 20, false]

解释
BSTIterator bSTIterator = new BSTIterator([7, 3, 15, null, null, 9, 20]);
bSTIterator.next();    // 返回 3
bSTIterator.next();    // 返回 7
bSTIterator.hasNext(); // 返回 True
bSTIterator.next();    // 返回 9
bSTIterator.hasNext(); // 返回 True
bSTIterator.next();    // 返回 15
bSTIterator.hasNext(); // 返回 True
bSTIterator.next();    // 返回 20
bSTIterator.hasNext(); // 返回 False

 

提示:

  • 树中节点的数目在范围 [1, 105] 内
  • 0 <= Node.val <= 106
  • 最多调用 105 次 hasNext 和 next 操作

 

进阶:

  • 你可以设计一个满足下述条件的解决方案吗?next() 和 hasNext() 操作均摊时间复杂度为 O(1) ,并使用 O(h) 内存。其中 h 是树的高度。

思路

  1. 维护一个栈,栈顶的节点都是已经遍历完左子树的节点,那么我们此时就只用将栈顶结点弹出并返回即可
// 初始化栈的阶段
const cacheStack = []
// 输出当前root节点需要先遍历输出root节点的左子树,所以先将当前节点压入栈保存,root指针移动到左子树
// 最终沿着左节点一路往下的此条链路被我们全部压入栈中
while (root) {
    cacheStack.push(root)
    root = root.left
}
// 栈顶的节点都是已经遍历输出完左子树的节点,那么我们此时就只用将栈顶结点弹出并返回即可
const ret = cacheStack.pop()
return ret.val
  1. 输出完当前弹出的栈顶节点后,不是继续弹出栈顶节点,因为弹出节点的右子树仍未遍历,所以第二部分代码应该更改如下
// 栈顶的节点都是已经遍历完左子树的节点,那么我们此时就只用将栈顶结点弹出并返回即可
const ret = cacheStack.pop()
// 右节点pNode
let pNode = ret.right
// 输出当前pNode节点需要先遍历输出pNode节点的左子树,重复栈初始化时的操作
while (pNode) {
    cacheStack.push(pNode)
    pNode = pNode.left
}
return ret.val
  1. cacheStack为空时那么我们已经没有可输出的当前节点,所以直接以!!this.cacheStack.length判断是否hasNext tip: 当cacheStackcacheStack.pop()将根节点弹出的瞬间会为空,但是如果根节点如果有右子树需要遍历时会将右子树沿着左节点一路往下的链路上的节点都推入栈,开始新一轮子遍历。javascript为单线程执行代码,不考虑并发

最终代码:

/**
 * @param {TreeNode} root
 */
var BSTIterator = function(root) {
    this.cacheStack = []
    while (root) {
        this.cacheStack.push(root)
        root = root.left
    }
};

/**
 * @return {number}
 */
BSTIterator.prototype.next = function() {
    if (this.cacheStack.length) {
        const ret = this.cacheStack.pop()
        let pNode = ret.right
        while (pNode) {
            this.cacheStack.push(pNode)
            pNode = pNode.left
        }
        return ret.val
    }
};

/**
 * @return {boolean}
 */
BSTIterator.prototype.hasNext = function() {
    return !!this.cacheStack.length
};