【问题标题】:Creating a singly linked list that contains the numbers within a given range of a BST创建一个单链表,其中包含 BST 给定范围内的数字
【发布时间】:2013-04-16 01:26:26
【问题描述】:

我有兴趣找到BST 中给定范围内的所有整数。我想知道如果 BST 是用节点创建的,我将如何去做,因此我必须使用单链表。返回的链表中项目的顺序无关紧要。

例如考虑如下所示的树,

范围是 [6, 13],那么列表应该包括 6->7->8->10->13 或 13->10->8->7->6。正如我所说,返回列表中的顺序无关紧要。

此外,运行时约束是 O(n),其中 n 是树中的节点数。

提前致谢!

【问题讨论】:

  • 只是一个注释,不应该是6->7->8->10->13吗? ...让我想一想两者之间的答案。

标签: c linked-list range binary-search-tree


【解决方案1】:

您只需使用 2 个堆栈即可做到这一点...

让我们从 2 个堆栈开始,1 个是简单的待办事项堆栈(例如包含您应该访问的节点),另一个是带有结果的堆栈。

从根节点开始,如果节点在范围内,则将其推送到结果堆栈,并将两个子节点都推送到待办事项堆栈。如果超出范围,可能会发生 2 种情况: 1.) 它小于我们范围内的最小值 -> 将它的右孩子推到待办事项堆栈上 2.)它大于我们范围内的最大值 -> pus 它是待办事项堆栈上的左孩子

好的,让我们将其创建为一些有用的算法:

List<BSTNode*> FindAllInRange(BSTNode* root, int low, int high)
{
    Stack<BSTNode*> todo;              //< Todo stack
    Stack<BSTNode*> results;           //< Results stack

    // Start with root node
    todo.push(root);

    // While we have nodes to process
    while(todo.size() > 0)
    {
        // Get top node, and pop it from stack
        BSTNode* curr = todo.top();
        todo.pop();

        // If its value is less than the lowest value in range
        if(curr->value < low)
        {
            // Push right children if exists (as it may be higher than lowest value in range)
            if(node->right)
                todo.push(node->right);
        }
        // If its value is greater than the highest value in range
        else if(curr->value > high)
        {
            // Push left children if exists (as it may be lower than highest value in range)
            if(node->left)
                todo.push(node->left);
        }
        // Otherwise (we're in range)
        else
        {
            // Push current node to results stack
            results.push(curr);

            // If right node exists, push it on todo stack
            if(node->right)
                todo.push(node->right);

            // If left node exists, push it on todo stack
            if(node->left)
                todo.push(node->left);
        }
    }

    // Now you just have to convert the stack to list (and possibly sort it, reverse sort it, ...)
    return results.ConvertToList();
}

请注意,这只是类似 C++ 语言的伪代码。

【讨论】:

  • 这是一个有趣的解决方案。我正在考虑使用递归(我认为这不是最佳选择,因为我使用的是 C,尤其是在树很大的情况下)。
  • 递归不会改变时间复杂度。但是在极端情况下,您确实会遇到堆栈溢出。但是,该算法可能存在一个问题。您说输出中的顺序无关紧要。但是,在您的示例中,答案都是排序的。现在,如果你需要维护秩序,这个算法将不会通过时间限制。你会得到分散的结果,排序需要 O(N log N)。否则修改为Bredth-first Graph Travesal。有利于避免递归。
【解决方案2】:

如果你对 BST 有基本的了解,你应该知道从树中检索一组已排序的元素实际上非常简单。熟悉LVR/RVL遍历的可以直接跳到“答案”。

循环遍历树:

遍历树通常被描述为三个字母LVR的组合。留下LR 是对的。 V 表示访问。

这描述了您在遍历树时遵循的模式。 L 表示如果存在,您将树向下提升到左侧节点。 R 对。 V 表示当前节点上的一些操作,如打印。它使用重复!这很重要。

现在。如何获得排序集。当访问意味着打印或推送时,很简单LVR

示例 - 完整的演练:

 (8) You start in root. `L` - go left. 
  (3) You are in (3). You go `LVR` for this node again - recurrence. `L`
    (1) You are in (1). Now *again* `LVR`.
    However there is no left node so we go to `V` - visit/print 1. Now `R` - no right node. End. By recurrence we go back to 3.
  (3) - We're done with `L`. We do `V`. Print 3.
  (3) - `R`.
    (6) You are in (6) - `LVR` again. 'L'
      (4) You are in (4) - `L` does not exists. Print 4. `R` does not exist. Back one level.
    (6) - `V`. Print 6.
    (6) - `R`.
      You are in (7) - `L` does not exists. Print 7. `R` does not exist. Back one level.
    (6) - `LVR` Done. Back one level.
  (3) - `LVR` Done. Back one level.
(8) - `R`.
  (10) You are in 10. `L` Does not exist.
  (10) `V`. Print 10.
  (10) `R`.
    (14) You are in 14.
    (14) `L`.
      (13) You are in 13. `L` does not exists. Print 14. `R` does not exist. Back one level.
    (14) `V`. Print 14.
    (14) `R`. Does not exist. Back one level.
  (10) Done with `R`. Back one level.
(8) Done with `R`. Back one level.
Haha we were on root node so we are done.

如果你会关注打印。事实证明,您按从低到高的顺序打印了整套。 RVL 模式会做类似的事情,但是由于您首先向右走,因此您将首先访问最右边的节点,因此顺序将是降序的。由于没有魔法并且您只访问每个节点一次,因此时间复杂度为O(n)

答案:

简单的方法。做一个普通的LVR 遍历。但仅在符合范围的情况下打印数字。 稍微困难但更好的平均和极端情况。找到起始节点。然后开始遍历,每次访问时只与上限进行比较,当节点数据超过上限时停止。

当然,您可以使用堆栈或其他东西(如列表)来按排序顺序存储元素,而不是打印。

【讨论】:

  • 我一直在考虑使用这种方法,但我对存储元素的方式感到困惑。不过我想通了。
  • 如果您传递对容器的引用或使用全局引用,这很容易。就像我说的,在访问步骤中,您可以使用任何您认为合适的东西。我认为这是最简单的算法,只要你了解递归和树遍历。此外,如果您的树不是双向的,您将难以避免再次发生。 IE。您需要记住您来自哪个节点或下一步要去哪里。只要你想对输出进行排序,就是这样。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-01-20
  • 2021-12-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-10-14
相关资源
最近更新 更多