【问题标题】:Kth largest element using in-order traversal without knowing the height of the binary search tree在不知道二叉搜索树高度的情况下使用中序遍历的第 K 个最大元素
【发布时间】:2017-03-07 08:13:13
【问题描述】:

我们可以在不知道二叉搜索树高度的情况下使用中序遍历找到第k大元素吗? 或者有没有办法让我们创建一个新的遍历模式,比如“RIGHT ROOT LEFT”

【问题讨论】:

  • 如果您的问题是我们可以通过先右后左而不是向下钻取来遍历二叉决策树,请先寻找呼吸搜索
  • 只需向后进行中序遍历。但是请注意,如果您在工作面试中并且面试官问您如何找到 BST 中的第 K 个最大元素,他会想知道您如何使其在 log(N) 时间内工作。
  • 感谢 cmets,我找到了一些方法来完成“右根左”遍历模式

标签: data-structures tree binary-search-tree tree-traversal


【解决方案1】:

我们可以在不知道二叉搜索树高度的情况下使用中序遍历找到第 k 个最大的元素吗?

是的,我们可以。

我们可以使用一些额外的空间来做到这一点。 我们不一定需要通过RIGHT-ROOT-LEFT 来找出kth 的最大元素(尽管这样做会避免使用额外的空间)。

有几种方法。

最基本的就是要有一个队列。

每当您按顺序遍历时,请继续在队列中插入值。 不用说,由于它是二叉搜索树,队列将被排序。 因此,kth 最大元素位于queue.size() - 1 - k 索引处。 (假设0th largrst 是最大元素,1stlargest 是第二个最大元素,依此类推) 无论k如何,这种方法都需要O(n)额外空间

为了优化使用的空间,我们可以使用k的信息
请注意,我们只需要 (queue.size() - 1 - k)th 索引处的元素。因此 我们可以有一个大小为(k+1) 的队列。在插入元素之前,我们会检查队列中是否有超过k 元素,如果有,我们会从前面移除一个元素,因为我们不需要它。
遍历完成后,kth 最大的元素将位于队列的最前面。

这是这两种方法的 java 实现:

import java.util.LinkedList;
import java.util.Queue;

class Node {
    int data;
    Node left;
    Node right;

    static Queue<Integer> efficient = new LinkedList<>();
    static Queue<Integer> complete = new LinkedList<>();

    Node(int n) {
        this.data = n;
        this.left = null;
        this.right = null;
    }

    public static void inorder(Node node, int k) {
        if (node == null) {
            return;
        }
        inorder(node.left, k);
        if (efficient.size() > k) {
            efficient.poll();
        }
        efficient.add(node.data);
        complete.add(node.data);
        System.out.println(efficient.toString());
        inorder(node.right, k);
    }

    public static void main(String[] args) {
        Node root = new Node(7);
        root.left = new Node(4);
        root.left.left = new Node(1);
        root.left.right = new Node(5);
        root.left.right.right = new Node(6);
        root.right = new Node(9);
        int k = 2;
        inorder(root, k);
        System.out.println("Efficient queue size : " + efficient.size() + " " + efficient.peek());
        System.out.println("Full queue size : " + complete.size() + " " + complete.toArray()[complete.size() - 1 - k]);

    }

}

运行此程序,您将知道队列增长的效率。

请注意,此处k 应小于树中的节点数。如果不是,那么答案就是最小的元素。

其他方法使用heap 以获得更通用的解决方案。在这种情况下,树不需要是二叉搜索树。

【讨论】:

  • 太棒了!但这需要一些额外的空间。我们可以做其他事情来只遍历 k 个节点吗(一旦达到 k,我们将显示该值)。
  • 不,我认为,因为 RIGHT-ROOT-LEFT (post order) 遍历是必需的。
  • 后序遍历是“左-右-根”(据我所知)。我刚刚提到 'RIGHT-ROOT-LEFT' 作为一个单独的模式(只是为了解决问题)。
【解决方案2】:

这个呢:

  • 设 x 为树的最小元素对应的节点(例如x = T.root; while (x-&gt;right != NULL) { x = x-&gt;right; }
  • 现在重复x = x.predecessor() k-1 次(显然,注意边缘情况)
  • return x(你找到了k-树中最大的元素)

在 BST 上实现前任和后继方法并不难,当节点有一个额外的指向父节点的指针时,这很容易。

【讨论】:

    【解决方案3】:

    你说的是简单的二叉树还是像 AVL 树这样的树?

    如果我理解正确的话,你根本不需要知道高度...... 中序遍历 - RIGHT ROOT LEFT - 应该是从最高值到最低值。

    每次你进入函数时,你可以增加一个静态变量(或向函数发送另一个参数)——直到你到达 K。

    您还可以维护一个度数树,并直接知道从哪里经过该树。

    【讨论】:

      猜你喜欢
      • 2018-09-06
      • 1970-01-01
      • 2011-02-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-25
      相关资源
      最近更新 更多