【问题标题】:Nth largest element in a binary search tree二叉搜索树中的第 N 个最大元素
【发布时间】:2011-02-06 10:17:34
【问题描述】:

如何在 BST 中找到第 N 个最大的节点?

在执行 BST 的按顺序遍历时是否保留计数变量?当count = N时返回元素???

【问题讨论】:

    标签: algorithm binary-tree


    【解决方案1】:

    提示:使用树的中序遍历。它可以按排序顺序打印出项目,因此您可以确定找到第 N 个最大的项目。在“行走”时保留一个计数器,每次“访问”一个节点时递增。

    编辑:虽然 IVlad 的回答确实更快,但它需要您在节点中保留额外的信息。这个答案不是,而是O(n)。只是指出这是您必须注意的权衡。

    【讨论】:

    • @IVlad:你的方法在 BST 节点中不需要额外的存储空间吗?
    • 不比你的多。你将如何在没有额外存储空间的情况下遍历树?递归堆栈也算作额外内存。
    • @IVlad:这不是重点。重点是修改 BST 节点以保存遍历的额外信息
    • @IVlad:因为这是一个家庭作业问题,而不是设计生产算法。这就是为什么有规则。我试图撤销我的反对票,但为时已晚,您必须编辑答案才能使其成为可能。
    • IVlad:您的解决方案需要 O(n) 额外存储空间:每个节点都需要计数。此解决方案仅需要 O(log n) 额外存储空间,以实现递归中使用的堆栈的最大深度。
    【解决方案2】:

    查看我的回答here。您可以平均在O(log n) 中执行此操作,其中 n = 节点数。如果树不平衡,最坏的情况仍然是O(n)(如果它是平衡的,则总是O(log n))。然而,为了遍历总是O(n)

    【讨论】:

    • 当然,这只有在您可以修改二叉树的节点时才有效。如果您使用的是库二叉树,则无法选择您的解决方案。
    • 我认为用所需的额外信息填充树会花费 O(n),不是吗?因此,如果给定的树没有附加信息,我看不出如何在 O(log n) 中解决它。
    • 它需要 O(n) 内存,但不需要任何额外的时间,只需要在内存中构建树所需的时间。
    【解决方案3】:

    使用倒序遍历。即先到右孩子而不是左孩子。 递归可以得到如下: 考虑递归解决方案时必须使用全局计数的最重要问题。

    reverseInorder(root){
     if(root!=null){
    reverseInorder(root->rightChild);
    self
    reverseInorder(root->leftChild);
    }
    }
    

    java中的解决方案

        package datastructure.binaryTree;
    
    import datastructure.nodes.BinaryTreeNode;
    
    
    public class NthElementFromEnd {
        private BinaryTree tree=null;
        int currCount=0;
        public NthElementFromEnd(int[] dataArray) {
            this.tree=new BinaryTree(dataArray);
    
        }
        private void getElementFromEnd(int n){
            getElementFromEnd(this.tree.getRoot(),n);
        }
        private void getElementFromEnd(BinaryTreeNode node,int n){
            if(node!=null){
                if(currCount<n)
                getElementFromEnd(node.getRightChild(),n);
                currCount++;
    
                if(currCount==n)
                {
                    System.out.print(" "+node.getData());
                    return;
                }
                if(currCount<n)
                getElementFromEnd(node.getLeftChild(),n);
            }
        }
    
        public static void main(String args[]){
            int data[]={1,2,3,4,5,6,7,8,9};
            int n=2;
            new NthElementFromEnd(data).getElementFromEnd(n);
        }
    }
    

    【讨论】:

      【解决方案4】:
      int nLargeBST(node *root, int N) {
          if (!root || N < 0) {
              return -1;
          }
          nLargeBST(root->left, N);
          --N;
          if(N == 0) {
              return root->val;
          }
          nLargeBST(root->right, N);
      }
      

      【讨论】:

        【解决方案5】:

        这段代码来自我的作业,其中一个条件是不使用 数组。为了使代码更紧凑和可读,您可以使用 字符串名称.split("|")。由于该方法是递归的,我使用 stringBuilder 它具有以下结构:“counter|orderOfElementToFind|dataInrequiredNode”

        protected StringBuilder t(StringBuilder s)
        {
            if (lc != null) 
            {
                lc.t(s);
        }
        
        
        if((s.toString().charAt(s.toString().length() - 1)) == '|')
        {
                String str = s.toString();
            s.delete(0, s.length());
        
                int counter = 0, k = 0;
        
        
                String strTemp = "", newStrBuilContent = "";
        
                for (int i = 0, c = 0 ; i < str.length(); ++i)
                {
                    if (c == 0)
                    {
                    if (str.charAt(i) != '|')
            {
                strTemp += str.charAt(i); 
            }
            else
            {
                counter = Integer.parseInt(strTemp);
                ++c;
        
                strTemp = "";
            }
                    }
            else
            {
        
                    if (str.charAt(i) != '|')
                {
                    strTemp += str.charAt(i); 
                    }
                else
                {
                        k = Integer.parseInt(strTemp);
                }
        
            }
        
            counter ++;
        
                    newStrBuilContent = (counter + "|" + k + "|");
            s.append(newStrBuilContent);
            if (counter == k)
            {
                double ldata = this.getData();
                s.append(ldata);
        
            }
        
        }
        
        if (rc != null) 
        {
            rc.t(s);
        } 
        
            return s;
        
        }
        

        以及方法调用:

        // the value of counter ad the beginning is 0 and data 
        // segment is missing
        String s = ("0|" + order +"|");
        StringBuilder strBldr = new StringBuilder(s); 
        String content = sTree.t(strBldr).toString();
        
        s = "";
        
        for (int i = 0, c = 0; i < content.length(); ++i)
        {           
            if (c < 2)
        {
            if (content.charAt(i) == '|')
            {  
                ++c;
                }
            }
        else
        {
            s += content.charAt(i);
        }
        }
            `
        

        【讨论】:

          【解决方案6】:

          您可以通过对二叉搜索树的中序遍历稍作修改来做到这一点 - 我们正在寻找第 k 个最大的元素;

              void kthLargest(Node node, int k, int count) {
          
                if(node != null) {
          
                   nthLargest(node.left,k,count); //traversing the left node
          
                   //while visit the node we do the following
                   count++; // increment the count and check if that is equal to k
                   if ( count == k ) {
                     System.out.println("Node found "+node.value);
                   } 
          
                   nthLargest(node.right,k,count); //traversing the right node
                }
          
              }
          

          但是这样的问题是你将到达第 k 个最小的元素,因此你的方法调用应该是这样的:第 k 个最大的元素 = (n-k)th 个最小的元素。

              nthLargest(root,n-k,0);
          

          【讨论】:

          • 这对每个新手来说都是一个小陷阱,因为这也是我很久以前的第一次尝试。你运行这段代码了吗?
          【解决方案7】:

          这个想法很简单:按照每个节点的值的递减顺序遍历树。当您到达第 N 个节点时,打印该节点值。这是递归代码。

          void printNthNode(Node* root, int N)
          {
             if(root == NULL)
                 return;
          
             static int index = 0; //These will initialize to zero only once as its static
          
             //For every Node go to the right of that node first.
             printNthNode(root->right, N);
          
          
             //Right has returned and now current node will be greatest
             if(++index == N)
             {
              printf("%d\n", root->data);
              return;
             }
          
             //And at last go to the left
             printNthNode(root->left, N);
          }
          

          编辑 - 根据下面的 cmets,由于静态局部变量,这看起来像是一次性调用函数。这可以通过为index 传递包装对象来解决,如下所示:

              class WrapIndex {
                   public: int index;
              };
          

          并且方法签名将更改为

          void printNthNode(Node* root, int N, WrapIndex wrapInd)

          现在,我们不需要局部静态变量;而是使用包装对象的index。调用看起来像

          WrapIndex wrapInd = new WrapIndex();
          wrapInd.index=0;
          printNthNode(root,7,wrapInd);
          
          wrapInd.index=0;
          printNthNode(root,2,wrapInd);
          

          【讨论】:

          • 这是一个严格意义上的one-time 仅供使用的功能。例如,如果 b 是一个 BSTree,printNthNode(b.head, 7); // prints the 7th largest number printNthNode(b.head, 2); // fails to print anything
          • @lifebalance 我怀疑这是因为 static 局部变量。我们可以为index 使用包装器对象,并在每次调用时传递它。对于每个新调用,我们都会将此包装对象的index 设置为0
          • 请提供一些包装器的示例代码作为您解决方案的一部分。谢谢,
          • @lifebalance 编辑了答案!
          • 您可能需要传入对wrapInd的引用,否则可能无法正常工作。或者,只需一个整数引用变量作为第三个参数printNthNode(Node*, int, int&amp;) 就足够了。电话将是int counter = 0; printNthNode (root, 7, counter); counter = 0; printNthNode(root, 2, counter);
          【解决方案8】:
          • 保持每个节点的子树大小(root.size 类似的东西)。例如 {2,3,1} 是根为 2 的二叉树,则节点 (2) 的大小为 3,节点 (1) 的大小为 1,节点 (2) 的大小为 1

          • 如果你想在根节点大小为 23 的树中找到第 4 个最大的元素,请考虑它的排名

          • 最大元素等级为 23,因为根节点大小为 23。所以第 4 大元素等级为 23-4+1=20

          • 所以我们必须在给定的树中找到排名第 20 的元素

          • 最初将 rank=0 标志声明为零

          • 从根节点开始找到它的等级(等级+左孩子的大小+1),例如左孩子的大小是16,那么根元素的等级是17(等级+左孩子的大小+1)

          • 所以我们必须寻找排名为 20 的元素。显然我们必须遍历它的右孩子

          • 遍历右孩子,根据上式求右孩子rank(根据上式,注意:现在rank flag值为17),根据rank决定是向右还是向左

          • 递归重复此过程,直到我们找到 rank==20

          【讨论】:

            【解决方案9】:

            BST 中的第 K 个最大元素。学习如何思考此类问题并使用递归解决。 Kth Larget Explanation Recursion

            【讨论】:

            • 链接需要更新。
            【解决方案10】:

            我会通过从最大元素到最小元素遍历树并在到达询问位置时返回值来做到这一点。我为第二大价值实施了类似的任务。 2 的值是硬编码的,但是否可以通过附加参数轻松更改:)

            void BTree::findSecondLargestValueUtil(Node* r, int &c, int &v)
            {
                if(r->right) {
                    this->findSecondLargestValueUtil(r->right, c, v);
                }
            
                c++;
            
                if(c==2) {
                    v = r->value;
                    return;
                }
            
                if(r->left) {
                    this->findSecondLargestValueUtil(r->left, c, v);
                }
            }
            
            
            int BTree::findSecondLargestValue()
            {
                int c = 0;
                int v = -1;
            
                this->findSecondLargestValueUtil(this->root, c, v);
            
                return v;
            }
            

            【讨论】:

              【解决方案11】:
              // C++ program to find k'th largest element in BST
              #include<iostream>
              using namespace std;
              
              struct Node
              {
                  int key;
                  Node *left, *right;
              };
              
              // A utility function to create a new BST node
              Node *newNode(int item)
              {
                  Node *temp = new Node;
                  temp->key = item;
                  temp->left = temp->right = NULL;
                  return temp;
              }
              
              // A function to find k'th largest element in a given tree.
              void kthLargestUtil(Node *root, int k, int &c)
              {
                  // Base cases, the second condition is important to
                  // avoid unnecessary recursive calls
                  if (root == NULL || c >= k)
                      return;
              
                  // Follow reverse inorder traversal so that the
                  // largest element is visited first
                  kthLargestUtil(root->right, k, c);
              
                  // Increment count of visited nodes
                  c++;
              
                  // If c becomes k now, then this is the k'th largest 
                  if (c == k)
                  {
                      cout << "K'th largest element is "
                           << root->key << endl;
                      return;
                  }
              
                  // Recur for left subtree
                  kthLargestUtil(root->left, k, c);
              }
              
              // Function to find k'th largest element
              void kthLargest(Node *root, int k)
              {
                  // Initialize count of nodes visited as 0
                  int c = 0;
              
                  // Note that c is passed by reference
                  kthLargestUtil(root, k, c);
              }
              
              /* A utility function to insert a new node with given key in BST */
              Node* insert(Node* node, int key)
              {
                  /* If the tree is empty, return a new node */
                  if (node == NULL) return newNode(key);
              
                  /* Otherwise, recur down the tree */
                  if (key < node->key)
                      node->left  = insert(node->left, key);
                  else if (key > node->key)
                      node->right = insert(node->right, key);
              
                  /* return the (unchanged) node pointer */
                  return node;
              }
              
              // Driver Program to test above functions
              int main()
              {
                  /* Let us create following BST
                            50
                         /     \
                        30      70
                       /  \    /  \
                     20   40  60   80 */
                  Node *root = NULL;
                  root = insert(root, 50);
                  insert(root, 30);
                  insert(root, 20);
                  insert(root, 40);
                  insert(root, 70);
                  insert(root, 60);
                  insert(root, 80);
              
                  int c = 0;
                  for (int k=1; k<=7; k++)
                      kthLargest(root, k);
              
                  return 0;
              }
              

              【讨论】:

                【解决方案12】:

                Swift 版本。这与 Vallabh Patade 所说的密切相关。当它试图通过一个没有子节点的节点时,计数器加 1。和他的有点不一样。

                class BinaryNode {
                    var val: Int
                    var left: BinaryNode?
                    var right: BinaryNode?
                
                    init(value: Int) {
                        self.val = value
                    }
                }
                
                func findMaxValue(_ n: Int, from root: BinaryNode?) {
                    var counter = 0
                    maxValue(counter: &counter, n: n, node: root)
                }
                
                private func maxValue(counter: inout Int, n: Int, node: BinaryNode?) {
                    if node == nil {
                        counter += 1
                        return
                    }
                    maxValue(counter: &counter, n: n, node: node?.right)
                    // If the counter has reached the nth node we're looking for.
                    if counter == n {
                        if let val = node?.val { print(val) }
                    }
                    maxValue(counter: &counter, n: n, node: node?.left)
                }
                

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2012-11-25
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2021-12-21
                  相关资源
                  最近更新 更多