【问题标题】:Binary Search Tree Sum of Differential Keys差分键的二叉搜索树和
【发布时间】:2017-04-16 16:00:22
【问题描述】:

我正在尝试在二叉搜索树类中实现一个名为 sigma() 的方法。此方法的工作是返回 BST 中差分键的总和。我给出的定义如下:

定义 1. 二叉树中节点的差分键 elements are integers 是节点中的元素,如果节点是 root or 是节点中的元素与其 父母。零节点的差分为 0。参见图 1 差分二叉树的插图。的总和 T 的差分密钥为 9,∑i=1n Δ(i),其中 Δ(i) 表示 节点i和n的差分键,树的大小。

该方法应返回树 sigma(T) 值的总和。所以在这种情况下,sigma(T) 将返回 10-4-2+3+5-3 = 9。我理解所有这些背后的概念,并且可以在纸上轻松地做到这一点,但是将它实现到我的代码中是我的我遇到了麻烦。我需要编写一个包装方法和一个递归的辅助方法来定义sigma()

这是我目前编写的 BSTree 类(sigma() 位于底部):

package bstreedemo;

import java.util.function.Function;

/**
 * A binary search tree <br>
 * Requires JDK 1.8 for Function
 * @param <E> the tree data type
 * @since 12/09/2016
 * @see BSTreeAPI
 */
public class BSTree<E extends Comparable<E>> implements BSTreeAPI<E>
{
   /**
    * the root of this tree
    */
   private Node root;
   /**
    * the number of nodes in this tree
    */
   private int size;
   /**
    * A node of a tree stores a data item and references
    * to the child nodes to the left and to the right.
    */    
   private class Node
   {
      /**
       * the data in this node
       */
      public E data;
      /**
       * A reference to the left subtree rooted at this node.
       */
      public Node left;
      /**
       * A reference to the right subtree rooted at this node
       */
      public Node right;
   } 
   /**
    *   Constructs an empty tree
    */      
   public BSTree()
   {
      root = null;
      size = 0;
   }
   @Override
   public boolean isEmpty()
   {
      return size == 0;
   }

   @Override
   public void insert(E item)
   {
      Node newNode = new Node();
      newNode.data = item;
      if (size == 0)
      {
         root = newNode;
         size++;
      }
      else
      {
         Node tmp = root;
         while (true)
         {
            int d = tmp.data.compareTo(item);
            if (d == 0)
            { /* Key already exists. (update) */
               tmp.data = item;
               return;
            }
            else if (d>0)
            {
               if (tmp.left == null)
               { /* If the key is less than tmp */
                  tmp.left = newNode;
                  size++;
                  return;
               }
               else
               { /* continue searching for insertion pt. */
                  tmp = tmp.left;
               }
            }
            else
            {
               if (tmp.right == null)
               {/* If the key is greater than tmp */
                  tmp.right = newNode;
                  size++;
                  return;
               }
               else
               { /* continue searching for insertion point*/
                  tmp = tmp.right;
               }
            }
         }
      }
   }

   @Override
   public boolean inTree(E item)
   {
      return search(item) != null;
   }

   @Override
   public void remove(E item)
   {      
      Node nodeptr = search(item);
      if (nodeptr != null)
      {
         remove(nodeptr);
         size--;
      }
   }

   @Override
   public void inorderTraverse(Function func)
   {
      inorderTraverse(root,func);
   }

   @Override
   public E retrieve(E key) throws BSTreeException
   {      
      if (size == 0)
         throw new BSTreeException("Non-empty tree expected on retrieve().");
      Node nodeptr = search(key);
      if (nodeptr == null)
         throw new BSTreeException("Existent key expected on retrieve().");
      return nodeptr.data;
   }

   @Override
   public int size()
   {
       return size;
   }   

   /**
    * A recursive auxiliary method for the inorderTraver method that
    * @param node a reference to a Node object
    * @param func a function that is applied to the data in each
    * node as the tree is traversed in order.
    */
   private void inorderTraverse(Node node, Function func)
   {
      if (node != null)
      {
         inorderTraverse(node.left,func); 
         func.apply(node.data);         
         inorderTraverse(node.right,func);
      }
   }

   /**
    * An auxiliary method that support the remove method
    * @param node a reference to a Node object in this tree
    */
   private void remove(Node node)
   {
      E theData;
      Node parent, replacement;
      parent = findParent(node);
      if (node.left != null)
      {
         if (node.right != null)
         {
            replacement = node.right;
            while (replacement.left != null)
               replacement = replacement.left;
            theData = replacement.data;
            remove(replacement);
            node.data = theData;
            return;
         }
         else
            replacement = node.left;            
      }
      else
      {       
         if (node.right != null)
            replacement = node.right;         
         else
            replacement = null;
      }
      if (parent==null)
         root = replacement;
      else if (parent.left == node)
         parent.left = replacement;
      else
         parent.right = replacement;      
   }  

   /**
    * An auxiliary method that supports the search method
    * @param key a data key
    * @return a reference to the Node object whose data has the specified key.
    */
   private Node search(E key)
   {
      Node current = root;
      while (current != null)
      {
         int d = current.data.compareTo(key);
         if (d == 0)
            return current;
         else if (d > 0)
            current = current.left;
         else
            current = current.right;
      }
      return null;
   }

   /**
    * An auxiliary method that gives a Node reference to the parent node of
    * the specified node
    * @param node a reference to a Node object
    * @return a reference to the parent node of the specified node
    */
   private Node findParent(Node node)
   {
      Node tmp = root;
      if (tmp == node)
         return null;
      while(true)
      {
         assert tmp.data.compareTo(node.data) != 0;
         if (tmp.data.compareTo(node.data)>0)
         {
            /* this assert is not needed but just
               in case there is a bug         */
            assert tmp.left != null;
            if (tmp.left == node)
               return tmp;
            tmp = tmp.left;
         }
         else
         {
            assert tmp.right != null;
            if (tmp.right == node)
               return tmp;
            tmp = tmp.right;
         }
      }
   }

   /********************* Method Begins Here **********************/

   /**
    * A wrapper method for a method that computes the
    * sum of the differential keys of this binary search tree.
    * @return the sum of the differential keys of this tree.
    */
   @Override
   public int sigma()
   {
       if (size == 0)
           return 0;
       if (root.data.getClass() != Integer.class)
           throw new IllegalArgumentException("Keys must be integers");
       return (Integer)root.data + sigma(root);
   }

   /** 
    * An auxiliary method that recursively computes the sum of
    * differential keys in the subtrees of the tree rooted at
    * the specified key.
    * @param subtreeRoot the root of a subtree of this tree
    * @return the sum of the differential keys of the left and
    * right subtrees
    */
   private int sigma(Node subtreeRoot)
   {
       if(subtreeRoot == null) 
           return 0;
       if(subtreeRoot.left != null) 
       {
           if(subtreeRoot.right != null) 
           {
               return (Integer)subtreeRoot.data + sigma(subtreeRoot.left) + sigma(subtreeRoot.right);
           }
           else
               return (Integer)subtreeRoot.data + sigma(subtreeRoot.left);
       }
       if(subtreeRoot.right != null)
           return sigma(subtreeRoot.right) - (Integer)subtreeRoot.data;

       return (Integer)subtreeRoot.data;
   }

   /********************* Method Ends Here **********************/

   /**
    * Determines whether this binary tree is perfect
    * @return true if the binary tree is perfect, otherwise false
    */
   @Override
   public boolean isPerfect()
   {

   }

   /**
    * A wrapper method that computes the height of this tree
    * @return the height of this tree
    */
   @Override
   public int height()
   {
       return height(root);
   }

   /**
    * An auxiliary method that recursively computes 
    * the height of the subtree rooted at the specified node.
    * @param node a root of a subtree
    * @return the height of this tree
    */
   private int height(Node node)
   {
       if(node == null)
           return 0;
       return 1 + Math.max(height(node.left), height(node.right));
   }   

   /**
    * Determines whether this binary tree is complete.
    * @return true if this binary tree is complete, otherwise false
    */
   @Override
   public boolean isComplete()
   {

   }

   /**
    * An auxiliary method that recursively determines whether 
    * the index of the subtree rooted at the specified node is
    * less than the size of this tree.
    * @param node a root of a subtree
    * @param index the index of this node
    * @return 
    */
   private boolean isComplete(Node node, int index)
   {

   }
}

wrapper 方法返回根节点的数据,将其转换为 Integer,与在根节点上执行的辅助方法返回的值相加。

我认为需要考虑三种情况:

  1. if(subtreeRoot == null)
  2. if(subtreeRoot.left != null &amp;&amp; subtreeRoot.right != null) // Parent node has left and right child nodes

  3. if(subtreeRoot.left != null || subtreeRoot.right != null) // Parent node has only left or right child node

这是我卡住的地方,案例 2 和 3。我知道目标是从父节点的值中减去左和/或右孩子的值以找到差异的值该子树的键,然后向下递归左和/或右剩余子树执行相同的操作并将结果相加。但我不知道从这里去哪里。我们不允许在项目的方法中添加参数/参数,所以(Node subtreeRoot)是辅助方法唯一允许的参数,而包装器方法不带参数。创建一个 Function 来简化问题是否有用,我的逻辑是否有缺陷等?感谢您提供任何帮助或进一步的解释,因为此时我有点迷茫,而我的教授也无济于事。

【问题讨论】:

  • 你以前写过递归代码吗?
  • 我们几周前才在课堂上开始递归,我在这个概念上遇到了一些麻烦。 @PM77-1
  • 这三种情况不用担心。您应该有一个案例:您所在的子树是否为空。以你的方式思考事情可能适用于二叉树,但想象一下,如果你正在处理一棵可以有任意数量的孩子的树。那就是疯狂。您想要的是使用当前子树及其父级的数据来调用每个递归调用。

标签: java recursion data-structures binary-tree binary-search-tree


【解决方案1】:

你一定是我班里的人...无论如何,你必须使用 findParent 方法,因为你没有在 sigma 方法中传递父节点。

 private int sigma(Node subtreeRoot) {
    int tot = 0;
    if (subtreeRoot == null) {
        return 0;
    }
    if (findParent(subtreeRoot) == null) {
        tot = sigma(subtreeRoot.left) + sigma(subtreeRoot.right);
    } else{
        tot = (Integer) subtreeRoot.data - (Integer) findParent(subtreeRoot).data
                + sigma(subtreeRoot.left) + sigma(subtreeRoot.right);
    }
    return tot;
}

使用 findParent 方法的 if 语句是因为 subtreeRoot 是树的根,因此该方法将返回 null,因此它没有父级。然后,当您为左右孩子调用 sigma 方法时,它们将继续执行 else 语句。

我坚持使用 isPerfect 方法。如果我创建一个辅助方法并使用递归,我可以做到这一点,但我们不应该这样做......

【讨论】:

  • 邓肯?谢谢,我有一种感觉,我必须使用它,但不知道如何去做。 @Shobosy
  • 是的,邓肯。 @梅森沃尔顿
  • 我为 isPerfect() 找到了这个:link @Shobosy
【解决方案2】:

正如 MrMcGreg 已经指出的那样,您正在使主要逻辑变得复杂。

  • 基本情况:如果传入的树为NULL,则返回0
  • 递归:返回总和
    • 节点值减去父节点值
    • sigma(左孩子)
    • sigma(右孩子)

由于节点与其父节点没有链接,因此您的递归函数需要将父节点的值传递给例程。你会得到从这个伪代码派生的代码:

int sigma_aux (node root, int parent_val) {
    if !node
        return 0
    else
        root_val = root->data
        return root_val - parent_val +
               sigma_aux(root->left , root_val) +
               sigma_aux(root->right, root_val)

那是;只是一个基本案例和一个递归案例。用铅笔和纸跟踪它,直到你理解它 - 然后在你的代码上下文中实现它。


OP 写道:

...类似

return (Integer) subtreeRoot.data - (Integer) root.data + 
    sigma(subtreeRoot.left) + sigma(subtreeRoot.right);

?我不明白如何获取父节点的值。一世 认为父节点是传递给 辅助方法和它的孩子是 subtreeRoot.left 和 subtreeRoot.right

控制传入辅助方法的内容;不要把你的第一个概念当作一个给定的。这就是你感到困惑的地方。简化一下:顶层 sigma 的唯一真正目的是将树根的数据传递给 sigma_aux,并进行最顶层的计算以返回外部世界。

int sigma(节点根){ root_val = root->数据;

在此之后,它看起来就像 sigma_aux

【讨论】:

  • 我们不允许在方法中添加参数。我的印象是subtreeRoot 是子树的父级。这不正确吗?有没有办法可以使用我定义的findParent() 方法? @prune
  • 已修改。辅助方法传递父级的值。 sigma 使用树根的值引导进程。
  • +1 我只是喜欢在看似复杂的问题中表现出来的简单解决方案。
  • @Prune 之类的 return (Integer) subtreeRoot.data - (Integer) root.data + sigma(subtreeRoot.left) + sigma(subtreeRoot.right);?我不明白如何获取父节点的值。我认为父节点是传递给辅助方法的subtreeRoot 节点,它的子节点是subtreeRoot.leftsubtreeRoot.right
  • 我将以下数字(按列出的顺序)插入一个新的空 BST:12, 10, 13, 8, 11, 6, 9, 4, 7, 2, 1, 3, 5。找到差分键和 sigma() 的预期返回值之和等于 6。我还跟踪了以下 BST:4, 2, 1, 3, 6, 5, 7 预期 sigma 值为 4,40, 25, 15, 10, 5, 20, 30, 35, 50, 45, 55, 60, 52, 42, 48, 27 预期 sigma 值为 29。使用 @ 987654333@ 为我提供了第二和第三棵树的正确 sigma 值,但不是第一棵。 @Prune
猜你喜欢
  • 2017-04-17
  • 1970-01-01
  • 1970-01-01
  • 2023-03-08
  • 2020-12-09
  • 1970-01-01
  • 1970-01-01
  • 2011-09-16
  • 1970-01-01
相关资源
最近更新 更多