【问题标题】:How to find the closest element to a given key value in a binary search tree?如何在二叉搜索树中找到最接近给定键值的元素?
【发布时间】:2022-01-26 01:41:41
【问题描述】:

给定一个以整数值作为键的 bst,我如何在 bst 中找到与该键最近的节点? BST 使用节点对象 (Java) 表示。最接近的将是例如 4,5,9,如果键是 6,它将返回 5 ..

【问题讨论】:

  • 问题似乎不完整。 BST 如何存储?他们所说的“最接近”的价值是什么意思?最小的三角洲?应该如何对待领带?
  • @Andrew White,BST 的存储方式有什么关系?
  • @Hec 它没有;那天我的大脑死了。

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


【解决方案1】:

像查找元素一样遍历树。当您这样做时,记录最接近您的键的值。现在,当您没有找到键本身的节点时,返回记录的值。

因此,如果您在以下树中查找键 3,您最终会在节点 6 上找不到匹配项,但您记录的值将是 2,因为这是所有节点中最接近的键你已经遍历了(2,7,6)。

                 2
              1      7
                   6   8

【讨论】:

    【解决方案2】:

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

    def searchForClosestNodeHelper(root, val, closestNode):
        if root is None:
            return closestNode
    
        if root.val == val:
            return root
    
        if closestNode is None or abs(root.val - val) < abs(closestNode.val - val):
            closestNode = root
    
        if val < root.val:
            return searchForClosestNodeHelper(root.left, val, closestNode)
        else:
            return searchForClosestNodeHelper(root.right, val, closestNode)
    
    def searchForClosestNode(root, val):
        return searchForClosestNodeHelper(root, val, None)
    

    【讨论】:

      【解决方案3】:

      遍历需要 O(n) 时间。我们可以从上到下进行吗?像这样的递归代码:

      Tnode * closestBST(Tnode * root, int val){
          if(root->val == val)
              return root;
          if(val < root->val){
              if(!root->left)
                  return root;
              Tnode * p = closestBST(root->left, val);
              return abs(p->val-val) > abs(root->val-val) ? root : p;
          }else{
              if(!root->right)
                  return root;
              Tnode * p = closestBST(root->right, val);
              return abs(p->val-val) > abs(root->val-val) ? root : p;
          }   
          return null;
      }
      

      【讨论】:

      • 函数可以返回null。如果 p = null,则 p->val 无效。其实最后一行“return null”是不可达的。
      • 这似乎实际上是 O(h),其中 h 是 BST 与 O(n) 的高度
      【解决方案4】:

      可以在 O(log*n*) 时间内解决。

      • 如果节点中的值与给定值相同,则为最近的节点;
      • 如果某个节点中的值大于给定值,则移动到左子节点;
      • 如果节点中的值小于给定值,则移动到右子节点。

      该算法可以用以下C++代码实现:

      BinaryTreeNode* getClosestNode(BinaryTreeNode* pRoot, int value)
      {
          BinaryTreeNode* pClosest = NULL;
          int minDistance = 0x7FFFFFFF;
          BinaryTreeNode* pNode = pRoot;
          while(pNode != NULL){
              int distance = abs(pNode->m_nValue - value);
              if(distance < minDistance){
                  minDistance = distance;
                  pClosest = pNode;
              }
      
              if(distance == 0)
                  break;
      
              if(pNode->m_nValue > value)
                  pNode = pNode->m_pLeft;
              else if(pNode->m_nValue < value)
                  pNode = pNode->m_pRight;
          }
      
          return pClosest;
      }
      

      您可以访问my blog了解更多详情。

      【讨论】:

      • 同意,这个迭代解决方案将在平均 O(log(n)) 时间和 O(1) 空间上运行,最坏情况下 O(n) 时间和 O(1) 空间。递归解决方案将是 O(log(n) 时间和 O(log(n)) 空间,这就是为什么迭代解决方案很好的原因,因为它避免向调用堆栈添加递归调用占用的空间更少。
      【解决方案5】:

      “左右遍历并找到最近的”方法的问题在于它取决于输入元素以创建 BST 的顺序。如果我们在 11 中搜索 BST 序列 22、15、16、6、14、3、1、90,上述方法将返回 15,而正确答案为 14。 唯一的方法应该是使用递归遍历所有节点,返回最接近的节点作为递归函数的结果。这将为我们提供最接近的值

      【讨论】:

      【解决方案6】:

      这可以使用 Queue 和 ArrayList 来完成。 队列将用于在树上执行广度优先搜索。 ArrayList 将用于以广度优先顺序存储树的元素。 这是实现相同的代码

      Queue queue = new LinkedList();
      ArrayList list = new ArrayList();
      int i =0;
      public Node findNextRightNode(Node root,int key)
      {
          System.out.print("The breadth first search on Tree : \t");      
          if(root == null)
              return null;
      
          queue.clear();
          queue.add(root);
      
          while(!queue.isEmpty() )
          {
              Node node = (Node)queue.remove();
              System.out.print(node.data + " ");
              list.add(node);
              if(node.left != null) queue.add(node.left);
              if(node.right !=null) queue.add(node.right);            
          }
      
          Iterator iter = list.iterator();
          while(iter.hasNext())
              {
                  if(((Node)iter.next()).data == key)
                  {
                      return ((Node)iter.next());
                  }               
              }
      
          return null;
      }
      

      【讨论】:

        【解决方案7】:
        void closestNode(Node root, int k , Node result) {
            if(root == null) 
            {
               return;      //currently result is null , so it  will be the result
            }
            if(result == null || Math.abs(root.data - k) < Math.abs(result.data - k) )
            {
              result == root;
            }
            if(k < root.data)
            {
            closestNode(root.left, k, result)
            } 
            else 
            {
                closestNode(root.right, k, result);
            }
        
        }
        

        【讨论】:

          【解决方案8】:

          下面的一个适用于我拥有的不同样本。

          public Node findNearest(Node root, int k) {
              if (root == null) {
                  return null;
              }
              int minDiff = 0;
              Node minAt = root;
              minDiff = Math.abs(k - root.data);
          
              while (root != null) {
                  if (k == root.data) {
                      return root;
                  }
                  if (k < root.data) {
                      minAt = updateMin(root, k, minDiff, minAt);
                      root = root.left;
                  } else if (k > root.data) {
                      minAt = updateMin(root, k, minDiff, minAt);
                      root = root.right;
                  }
          
              }
              return minAt;
          }
          
          private Node updateMin(Node root, int k, int minDiff, Node minAt) {
              int curDif;
              curDif = Math.abs(k - root.data);
              if (curDif < minDiff) {
                  minAt = root;
              }
              return minAt;
          }
          

          【讨论】:

            【解决方案9】:

            这是在 BST 中查找最近元素的完整 Java 代码。

                    package binarytree;
            
                    class BSTNode {
                        BSTNode left,right;
                        int data;
            
                        public BSTNode(int data) {
                            this.data = data;
                            this.left = this.right = null;
                        }
                    }
            
                    class BST {
                        BSTNode root;
            
                        public static BST createBST() {
                            BST bst = new BST();
                            bst.root = new BSTNode(9);
                            bst.root.left = new BSTNode(4);
                            bst.root.right = new BSTNode(17);
            
                            bst.root.left.left = new BSTNode(3);
                            bst.root.left.right= new BSTNode(6);
            
                            bst.root.left.right.left= new BSTNode(5);
                            bst.root.left.right.right= new BSTNode(7);
            
                            bst.root.right.right = new BSTNode(22);
                            bst.root.right.right.left = new BSTNode(20);
            
                            return bst;
                        }
                    }
            
                    public class ClosestElementInBST {
                        public static void main(String[] args) {
                            BST bst = BST.createBST();
                            int target = 18;
                            BSTNode currentClosest = null;
                            BSTNode closestNode = findClosestElement(bst.root, target, currentClosest);
            
                            if(closestNode != null) {
                                System.out.println("Found closest node: " + closestNode.data);
                            }
                            else {
                                System.out.println("Couldn't find closest node.");
                            }
                        }
            
                        private static BSTNode findClosestElement(BSTNode node, int target, BSTNode currentClosest) {
                            if(node == null) return currentClosest;
            
                            if(currentClosest == null || 
                                    (currentClosest != null && (Math.abs(currentClosest.data - target) > Math.abs(node.data - target)))) {
                                currentClosest = node;
                            }
            
                           if(node.data == target) return node;
            
                            else if(target < node.data) {
                                return findClosestElement(node.left, target, currentClosest);
                            }
            
                            else { //target > node.data
                                currentClosest = node;
                                return findClosestElement(node.right, target, currentClosest);
                            }
                        }
            
                    }
            

            【讨论】:

              【解决方案10】:

              这是java中的工作解决方案,它使用BST的特性和附加整数来存储最小差

              public class ClosestValueBinaryTree {
                      static int closestValue;
              
                      public static void closestValueBST(Node22 node, int target) {
                          if (node == null) {
                              return;
                          }
                          if (node.data - target == 0) {
                              closestValue = node.data;
                              return;
                          }
                          if (Math.abs(node.data - target) < Math.abs(closestValue - target)) {
                              closestValue = node.data;
                          }
                          if (node.data - target < 0) {
                              closestValueBST(node.right, target);
                          } else {
                              closestValueBST(node.left, target);
                          }
                      }
                  }
              

              运行时间复杂度 - O(logN)

              时空复杂度 - O(1)

              【讨论】:

                【解决方案11】:

                鉴于提供了二叉搜索树,有效的方法是遍历树并比较根节点与目标的绝对差(距离),同时跟踪“距离”较小的节点以更新@987654322 @value 当我们遇到更接近目标值的节点时。接下来,我们可以开始将当前节点的值与目标进行比较,如果它小于目标我们要在右子树中搜索大于或等于根节点的值,如果当前节点值大于我们要在左子树中搜索严格小于根节点的值的目标。

                这样做,我们可以在每一步(平均)消除一半的 BST,这意味着我们遍历左子树(消除右半部分)或遍历右子树(消除左半部分),而跟踪最近的节点并在我们找到更接近目标的节点时更新它。

                对于你提供的5 4 9的BST,确实满足BST的要求:

                • 根节点左侧的所有值都严格小于根节点
                • 根节点右边的所有值都大于等于根节点
                • 每个父节点最多只能有两个子节点
                  5
                 / \
                4   9
                

                对于上下文,BST 中的节点将具有结构:

                struct Node {
                  int data;
                  Node *left;
                  Node *right;
                  Node() { data = 0; left = right = nullptr; };
                  Node(int val) { data = val; left = right = nullptr; };
                }
                

                以下是一些 C++ 解决方案,但这些逻辑可以很容易地与 Java 语法一起使用。

                递归方法平均在 O(log(n)) 时间和 O(log(n)) 空间执行,因为我们递归调用 minDiffHelper 并将这些调用或“帧”添加到占用的调用堆栈中空间。

                // On average: O(log(n)) time and O(log(n)) space
                // Worst case: O(n) time and O(n) space
                // where n = number of nodes in the tree
                
                int minDiffHelper(Node *root, int target, int closest);
                
                int minDiff(Node *root, int target) {
                    return minDiffHelper(root, K, root->data);
                }
                
                int minDiffHelper(Node *root, int target, int closest) {
                    if (abs(target-closest) > abs(target-root->data)) {
                        closest = root->data;
                    }
                    if (root->left != nullptr && root->data > target) {
                        return minDiffHelper(root->left, target, closest);
                    } else if (root->right != nullptr && root->data < target) {
                        return minDiffHelper(root->right, target, closest);
                    } else {
                        return closest;
                    }
                }
                

                迭代方法也平均在 O(log(n)) 时间执行,并且不向调用堆栈添加任何递归调用,因此我们只消耗恒定的 O(1) 空间而不是 O(log(n) 空间当我们递归添加到调用堆栈时,我们会看到。

                两种算法(递归和迭代)在最坏情况下都有 O(n) 空间,或者可以写成 O(d) 空间,其中 d = 树的深度。

                // On average: O(log(n)) time and O(1) space
                // Worst case: O(n) time and O(n) space
                // where n = number of nodes in the tree
                
                int minDiffHelper(Node *root, int target, int closest);
                
                int minDiff(Node *root, int target) {
                    return minDiffHelper(root, target, root->data);
                }
                
                int minDiffHelper(Node *root, int target, int closest) {
                    Node *current = root;
                    while (current != nullptr) {
                        if (abs(target-closest) > abs(target-current->data)) {
                            closest = current->data;
                        }
                        if (current->left != nullptr && current->data > target) {
                            current = current->left;
                        } else if (current->right != nullptr && current->data < target) {
                            current = current->right;
                        } else break;
                    }
                    return closest;
                }
                

                GeeksForGeeks 有一个很好的practice problem 来测试你的理解。它要求一个稍微不同的解决方案(返回最近元素到给定目标的距离),但这可以通过返回距离abs(target-closest) 而不是简单地返回节点值closest 来轻松处理。

                【讨论】:

                  猜你喜欢
                  • 2021-12-30
                  • 2021-01-28
                  • 2021-12-28
                  • 2011-03-10
                  • 2020-12-19
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多