【问题标题】:Coding an iterator to traverse binary tree编写一个迭代器来遍历二叉树
【发布时间】:2012-12-14 13:05:13
【问题描述】:

问题是,给定一个 BST,找出是否有两个数字相加等于给定数字 k。不应使用额外的内存。

现在,如果它是一个排序数组,我可以简单地保留两个指针,一个在开头,一个在结尾。在每一步,我都会计算指针指向的两个数字的总和,如果总和小于 k,我会递增起始指针,否则递减结束指针,直到匹配或指针重叠。

我可以对 BST 做同样的事情,通过中序遍历将其转换为排序数组,但这需要额外的内存。所以我认为迭代器解决方案是有序的。我会保留两个迭代器,一个会以正常的中序方式遍历 BST,调用它将返回下一个更大的数字,另一个会以逆序方式遍历 BST,在每次调用时返回下一个较小的数字。

知道如何设计这样的迭代器吗?我更喜欢 Python/Javascript 的解决方案。虽然 Python 提供了 iter 这样的函数,但我想使用闭包来设计它。

【问题讨论】:

  • 您可以按顺序遍历 BST 而无需将其复制到数组中——这只是深度优先搜索。你问怎么写?
  • 我不知道他们是否会提供帮助,但 Nicholas Zakas 有一个两部分的关于 BST 的 Javascript 系列:nczonline.net/blog/tag/binary-search-tree
  • @katrielalex,不完全是。我正在寻找一个迭代器,它会记住最后访问的节点,以便在后续调用时返回下一个节点..
  • @Cupidvogel,yield 专为此类任务而设计。

标签: javascript python closures binary-tree binary-search-tree


【解决方案1】:

这种迭代器(使用闭包)在 python 中称为生成器。

生成器是函数,使用“yield”关键字而不是“return”。当遇到yield时,返回各自的值,但函数的执行状态被暂停,直到需要下一个值。

因此,您只需实现一个树遍历函数,使用“yield”而不是“return”,即可实现您的目标。

它们很容易设计:

# Simple tree definition
class Tree:
    def __init__(self, data, left=None, right=None):
        self.data = data
        self.left = left
        self.right = right

# In-order lazy iterator (aka generator)
def inorder(tree):
    if tree is not None:
        for x in inorder(tree.left):
            yield x
        yield tree.data
        for x in inorder(tree.right):
            yield x

# Reverse in-order lazy iterator
def rev_inorder(tree):
    if tree is not None:
        for x in rev_inorder(tree.right):
            yield x
        yield tree.data
        for x in rev_inorder(tree.left):
            yield x

# Construct a tree
n1 = Tree(1)              
n2 = Tree(2)              
n3 = Tree(3)              #         7
n4 = Tree(4)              #       /   \
n5 = Tree(5, n1, n2)      #     5       6
n6 = Tree(6, n3, n4)      #    / \     / \
n7 = Tree(7, n5, n6)      #   1   2   3   4

for i in inorder(n7):
    print i, 
print

for i in rev_inorder(n7):
    print i, 
print

输出:

1 5 2 7 3 6 4
4 6 3 7 2 5 1

要手动迭代,请使用:

gen = rev_inorder(n7)
print gen.next()       # Output 4
print gen.next()       # Output 6

【讨论】:

  • 嗯,这很简洁,但我一直在寻找不使用itertoolsyield 之类的解决方案。这可行吗?
  • @Cupidvogel,您要求使用闭包的解决方案,但这就是产量所代表的。这是在 python 中做这些事情的正确方法。
  • looking for a solution without using itertools or yield kind of stuff 很傻。这就是你在 Python 中编写迭代器的方式。
  • 注意顺便说一句,递归解决方案虽然更优雅,但不一定是最好的,因为 Python 不擅长递归。具体来说,这不再是常量空间,因为每个递归调用都会生成一个堆栈帧。您可以通过迭代遍历来编写一个常量空间版本:去左边的孩子,除非它不存在,然后是右边的孩子,然后向上;如果你来自正确的孩子,那就再往上走。
  • 另请注意,to manually iterate 在这种情况下是正确的,但通常使用for 循环遍历迭代器。
【解决方案2】:

我想出了一个简单的想法 ;-): 如果您不想为 BST 周围的迭代分配额外空间,则需要牺牲性能。

var inst = new BST();
inst.Insert(-121);
inst.Insert(13);
inst.Insert(1);
inst.Insert(10);
GetAddends(inst, 55); // here we go
function GetAddends(bst, target) {
    var iter1 = bst.GetIterator();
    var iter2 = bst.GetIterator(false);

    while (iter1.IsValid() && iter2.IsValid() && (iter1.Pos() < iter2.Pos())) {
        var temp = iter1.data() + iter2.data();
        if (temp < target) iter1.Next();
        else if (temp > target) iter2.Next();
        else window.alert(iter1.data() + "+" + iter2.data() + "=" + target);
    }
    bst.ClearIterators();
}

function BST() {
    var _root, _nodeCount, _locked;
    this.Insert = function(data) { 
        if (_locked === true) throw "could not modify the BST during iteration";
        _nodeCount++;
    }
    this.Delete = function(data) {
        if (_locked === true) throw "could not modify the BST during iteration";
        _nodeCount--;
        return null;
    }
    this.GetIterator = function(isForward) { return Iter(isForward); }
    this.ClearIterators = function() { _locked = false; }

    function Iter(isForward) {
        if (isForward == null) isForward = true; // if the parameter is omitted, then true by default
        _locked = true;
        var _pos = isForward ? 0 : (_nodeCount - 1);
        var _curData;
        return function() {
            this.IsValid() {
                return (isForward ? (_pos < _nodeCount) : (_pos >= 0));
            }
            this.Next = function() {
                isForward ? _pos++ : _pos--;
                _curData = null;
            }
            this.Pos = function() { return _pos; }
            this.Data = function() {
                if (_curData == null) { /* loop the BST and find _posTH node and stored in the _curData in case we need it later */ }
                return _curData;
            }
        }
    }
}

代码没有实现 BST,但思路应该很清楚。玩得开心!在这里提醒一下,我们不允许在持有迭代器的过程中修改 BST。在我们不在持有迭代器的范围内之后,我们需要调用ClearIterators()。然而,一个优雅的解决方案可能是使用 PUB/SUB 让 BST 知道有多少迭代器存在。也许这可能是另一个问题,哈。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-24
    • 2012-01-01
    • 2022-11-11
    • 1970-01-01
    相关资源
    最近更新 更多