【问题标题】:Second max in BSTBST 中的第二个最大值
【发布时间】:2012-07-11 04:03:13
【问题描述】:

这是一道面试题。求 BST 中的第二个最大值。

最大元素是 BST 中最右边的叶子。第二个最大值是它的父节点或左子节点。所以解决方法是遍历BST找到最右边的叶子,并检查它的父母和左孩子。

这有意义吗?

【问题讨论】:

  • The max element is the rightmost leaf in the BST. 不,“常规”BST 在每个节点中都有一个键,它是“最右边的节点”,但不是叶子:将仅包含根和叶子的树视为左孩子(毫无疑问是最右边的孩子)。 (有“叶子搜索树”,所有有效值都在叶子上(想想字符串键和只携带前缀的节点,允许决定左或右)。)

标签: algorithm data-structures language-agnostic binary-search-tree


【解决方案1】:

不,那是错误的。考虑一下这个 BST:

        137
       /
      42
       \
        99

这里,第二个到最大值是最大值的左孩子的最右边的孩子。您的算法将需要更新,以便您检查最大值的父项,或最大值的左子项的最右边的子项。

另外,请注意 max 不一定是最右边的 leaf 节点,它是树的右脊椎底部的节点。上面,137 不是一片叶子。

希望这会有所帮助!

【讨论】:

  • 谢谢。我认为 BST 总是平衡,但它不正确。
  • 99 (root) / 42 (left child of 99) \ 137 (right child of 42) 这不是 BST 吗?在这种情况下,最大值不是树右脊柱底部的节点。
  • @ArchitVerma 我不认为这是一个有效的 BST,因为如果 137 是 42 的右孩子,而 42 是 99 的左孩子,那么 137 必须小于 99,它是't。
【解决方案2】:

回想一下,您可以通过修改inorder traversal 以相反的顺序列出 BST 的节点,首先探索右子树。这导致了一个简单的算法:

Node rightmost = findRightmostNode(root)
if (rightmost.left != null) {
    return findRightmostNode(rightmost.left)
else{
    return rightmost.parent
}

如果树只有一个元素,它将返回 null。

【讨论】:

  • 没问题! ;) 你可能可以通过在 if 语句之外调用 findRightmostNode(root) 来使它更好。本质上,您需要找到最右侧元素的左子树或父级的最右侧元素。本质上: 1. 从根中找到最右边的元素。 2. 如果该元素没有左子树,则返回父元素。如果它有左子树,则返回左子树最右边的元素。
  • @alvarosg 欢迎您提出修改建议,我会尽快批准。
  • 作为最右边节点的根真的值得特别处理吗?我不这么认为(可能需要findRightmostNode() 接受(&return)null)——如果你把它拿出来,与user2268025's 10 month younger, "uncommented" answer 的其余区别是开放编码findRightmostNode()
  • @greybeard 是的,我想只有这么多方法可以去 BST 的最后一个节点,然后去上一个。我建议进行更正,因为它位于顶部,并且大多数人都会阅读。
  • (@alvarosg第四版 for return rightmost.left != null ? findRightmostNode(rightmost.left) : rightmost.parent.)
【解决方案3】:
public static int findSecondLargestValueInBST(Node root)
    {
        int secondMax;
        Node pre = root;
        Node cur = root;
        while (cur.Right != null)
        {
            pre = cur;
            cur = cur.Right;
        }
        if (cur.Left != null)
        {
            cur = cur.Left;
            while (cur.Right != null)
                cur = cur.Right;
            secondMax = cur.Value;
        }
        else
        {
            if (cur == root && pre == root)
                //Only one node in BST
                secondMax = int.MinValue;
            else
                secondMax = pre.Value;
        }
        return secondMax;
    }

【讨论】:

    【解决方案4】:

    时间复杂度 O(logN) 和空间复杂度 O(1) 的更简单的迭代方法

    public static void main(String[] args) {    
            BinaryTreeNode result=isBinarySearchTree.secondLargest(rootNode);
    
                System.out.println(result.data);
            }
    
            private BinaryTreeNode secondLargest(BinaryTreeNode node) {
    
                BinaryTreeNode prevNode=null; //2nd largest Element
                BinaryTreeNode currNode=node;
                if(null == currNode)
                    return prevNode;
    
                while(currNode.right != null){
                    prevNode=currNode;
                    currNode=currNode.right;
                }
                if(currNode.left != null){
                    currNode=currNode.left;
                    while(currNode.right != null){
                        currNode=currNode.right;
                    }
                    prevNode=currNode;
                }
    
                return prevNode;
    
            }
    

    【讨论】:

      【解决方案5】:

      算法可以如下

      1. find the largest number in the tree. 
      
        private static int findLargestValueInTree(Node root) {
          while (root.right != null) {
           root = root.right;
          }
          return root.data;
        }
      
      2. Find the largest number in the tree that is smaller than the number we found in step 1
      
       public static int findSecondLargest(Node root, int largest, int current) {
         while (root != null) {
          if (root.data < largest) {
            current = root.data;
            root = root.right;
          } else {
            root = root.left;
         }
         }
        return current;
       }
      

      'current' 跟踪当前最大的数字,它小于在 step1 中找到的数字

      【讨论】:

        【解决方案6】:

        简单的 javascript 实现。

        function Node (value, left, right) {
            this.value = value;
            this.left = left;
            this.right = right; 
        }
        
        function second (node, prev, wentLeft) {
            if (node.right) {
                return second(node.right, node, wentLeft);
            } else if (node.left) {
                return second(node.left, node, true);
            } else {
                if (wentLeft) return node.value;
                return prev.value;
            }
        }
        second(root);
        

        【讨论】:

        • 它不适用于按以下顺序插入元素的 BST:5、4、3。
        【解决方案7】:

        一种遍历变体:

           public Tree GetSecondMax(Tree root)
            {
                Tree parentOfMax = null;
        
                var maxNode = GetMaxNode(root, ref parentOfMax);
        
                if (maxNode == root || maxnode.left != null)
                {
                    // if maximum node is root or have left subtree, then return maximum from left subtree
                    return GetMaxNode(maxnode.left, ref parentOfMax);
                }
        
                // if maximum node is not root, then return parent of maximum node
                return parentOfMax;
            }
        
            private Tree GetMaxNode(Tree root, ref Tree previousNode)
            {
                if (root == null || root.right == null)
                {
                    // The most right element have reached
                    return root;
                }
        
                // we was there
                previousNode = root;
        
                return GetMaxNode(root.right, ref previousNode);
            }
        

        【讨论】:

          【解决方案8】:
          int getmax(node *root)
          {
              if(root->right == NULL)
              {
                  return root->d;
              }
              return getmax(root->right);
          }
          
          
          int secondmax(node *root)
          {
              if(root == NULL)
              {
                  return -1;
              }
          
              if(root->right == NULL && root->left != NULL)
              {
                  return getmax(root->left);
              }
          
              if(root->right != NULL)
              {
                  if(root->right->right == NULL && root->right->left == NULL)
                  {
                      return root->d;
                  }
              }
          
              return secondmax(root->right);
          }
          

          【讨论】:

            【解决方案9】:

            一种非常直观的思考方式是考虑以下两种情况。 设第二大节点为S,最大节点为L。

            i) S 比 L “更早”地插入 BST。 ii) S 比 L“晚”插入 BST。

            对于第一种情况,很明显 L 是 S 的右孩子。这是因为除了 L 之外的任何节点都小于 S,所以不会放在 S 的右边。所以当 L 被说白了就是S的右孩子。

            对于第二种情况,当 S 被插入时,L 将是 BST 中最右边的节点。显然,L 不会有正确的孩子,因为它是最大的。但是,L 可以有自己的左子树。当S插入时,S会沿着“右路径”直到遇到L,然后左转去L的左子树。这里,我们知道L的左子树中的所有节点都小于S,所以S将是子树中最右边的节点。

            【讨论】:

              【解决方案10】:
              int getSecondLargest(Node root){
                  if(root==null)
                      return 0;
                  Node curr=root;
                  Node prev=root;
                  //Go to the largest node
                  while(curr.right != null){
                      prev = curr;
                      curr= curr.right;
                  }
                  //If largest Node has left child, Then largest element of tree with its root as largest.left will be the second largest number.
                  if(curr.left == null)
                      return prev.data;
                  else
                      return findLargest(curr.left);
              }
              
              int findLargest(Node root){
                  // No need toi check for null condition as in getSecondLargest method, its already done.
                  Node curr=root;
                  //To find largest just keep on going to right child till leaf is encountered.
                  while(curr.right != null){
                      curr = curr.right;
                  }
                  return curr.data;
              }
              

              【讨论】:

                【解决方案11】:

                我会通过从最大元素到最小元素遍历树并在到达询问位置时返回值来做到这一点。我为第二大价值实施了类似的任务。

                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;
                }
                

                【讨论】:

                  【解决方案12】:

                  您已接近正确答案。

                  这是我对直观答案的尝试。

                  最大的节点是最右边的节点。

                  最右边节点的左子树下的任何元素都大于除最右边节点之外的所有元素。因此这个子树中最大的节点就是答案。

                  如果没有左子树,则最右节点的父节点是大于除最右节点之外的所有其他节点的父节点。

                  【讨论】:

                    【解决方案13】:

                    我们的想法是一直向右走,直到右边没有任何东西。如果有左边,就走,然后一直向右走。如果您向左走,答案是遇到的最后一个节点。否则答案是您遇到的倒数第二个节点。

                    这是Java中的递归解决方案:

                    public TreeNode getSecondLargest(TreeNode root) {
                        if(root == null || (root.left == null && root.right == null))
                            throw new IllegalArgumentException("The tree must have at least two nodes");
                        return helper(root, null, false);
                    }
                    
                    private TreeNode helper(TreeNode root, TreeNode parent, boolean wentLeft) {
                        if(root.right != null) return helper(root.right, root, wentLeft);
                        if(root.left != null && !wentLeft) return helper(root.left, root, true);
                    
                        if(wentLeft) return root;
                        else return parent;
                    }
                    

                    时间复杂度为 O(lg n)。

                    【讨论】:

                      【解决方案14】:

                      在 BST 中查找 2nd Max Element 的算法将花费时间复杂度:O(n) --> 在树是右斜树的最坏情况下。 如果树是平衡树,那么时间复杂度是 O(height) 即 O(log n) 空间复杂度也一样:O(n) --> 在最坏的情况下 O(log n) --> 当树平衡时。

                      public TreeNode secondMax(TreeNode root){
                          return helper(root,null);
                      }
                      private TreeNode helper(TreeNode root,TreeNode prev){
                          if(root==null)
                              return root;
                          if(root.right==null && root.left!=null)
                              return root.left;
                          else if(root.right==null && root.left==null)
                              return prev;
                          return helper(root.right,root);
                      }
                      

                      【讨论】:

                      • 我猜2ndMax() 总是返回null。 (看看有趣的“语法”装饰。)
                      • 好吧,但它仍然返回错误的节点 - 尝试(a, ., (z, (b, ., (y,.,.), .)) (key, left child, right child;. 没有孩子)。
                      【解决方案15】:

                      此算法在树上运行一次并返回最大的项目Item1 和第二大的项目Item2。 排序调用是O(1),因为它们与树的大小无关。 所以总的时间复杂度O(N)空间复杂度O(log(N))时树是平衡的。

                      public static Tuple<int, int> SecondLargest(TreeNode<int> node)
                      {
                          int thisValue = node.Value;
                          if ((node.Left == null || node.Left.Right == null) && node.Right == null)
                          {
                              return new Tuple<int, int>(thisValue, -int.MaxValue);
                          }
                          else if (node.Left == null || node.Left.Right == null)
                          {
                              Tuple<int, int> right = SecondLargest(node.Right);
                              List<int> sortLargest = new List<int>(new int[] { right.Item1, right.Item2, thisValue });
                              sortLargest.Sort();
                              return new Tuple<int, int>(sortLargest[2], sortLargest[1]);
                          }
                          else if (node.Right == null)
                          {
                              Tuple<int, int> left = SecondLargest(node.Left.Right);
                              List<int> sortLargest = new List<int>(new int[] { left.Item1, left.Item2, thisValue });
                              sortLargest.Sort();
                              return new Tuple<int, int>(sortLargest[2], sortLargest[1]);
                          }
                          else
                          {
                              Tuple<int, int> left = SecondLargest(node.Left.Right);
                              Tuple<int, int> right = SecondLargest(node.Right);
                              List<int> sortLargest = new List<int>(new int[] { left.Item1, left.Item2, right.Item1, right.Item2, thisValue });
                              sortLargest.Sort();
                              return new Tuple<int, int>(sortLargest[4], sortLargest[3]);
                          }
                      }
                      

                      【讨论】:

                        猜你喜欢
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 2022-08-23
                        • 2021-01-31
                        • 2016-08-20
                        • 2021-12-04
                        • 1970-01-01
                        • 1970-01-01
                        相关资源
                        最近更新 更多