📜  在二进制搜索树中查找最接近的元素|节省空间的方法

📅  最后修改于: 2021-05-24 23:54:36             🧑  作者: Mango

给定二叉搜索树和目标节点K。任务是找到具有给定目标值K的绝对差最小的节点。

注意:使用的方法应具有恒定的额外空间消耗O(1)。不应使用递归或类似堆栈/队列的容器。

例子:

Input:  k = 4
Output:  4

Input:  k = 18
Output:  17

本文中提到的一个简单解决方案是使用递归来获取与Binary搜索树中的键最接近的元素。由于递归,上述帖子中使用的方法消耗O(n)额外空间。

现在,我们可以使用Morris遍历轻松地修改上述方法,Morris遍历是一种空间有效的方法,可以在不进行常量空间O(1)的递归或堆栈/队列的情况下进行有序树遍历。

Morris遍历基于Threaded Binary树,该树利用树中的NULL指针使它们指向某些后继或前任节点。就像在具有n个节点的二叉树中一样,n + 1个NULL指针浪费了内存。

在下面提到的算法中,我们只进行有序树遍历,而在使用Morris遍历进行有序树遍历时,我们检查节点数据和键之间的差异,并维护两个变量“ diff”和“ closest”,当找到更接近的变量时会更新密钥的节点。完成完整的有序树遍历后,我们得到了最近的节点。

算法

1) Initialize Current as root.

2) Initialize a variable diff as INT_MAX.

3)initialize a variable closest(pointer to node) which 
  will be returned.

4) While current is not NULL:

  4.1) If the current has no left child:
     a) If the absolute difference between current's data
        and the key is smaller than diff:
       1) Set diff as the absolute difference between the 
          current node and the key.
       2) Set closest as the current node. 

     b)Otherwise, Move to the right child of current.

  4.2) Else, here we have 2 cases:

   a) Find the inorder predecessor of the current node. 
      Inorder predecessor is the rightmost node 
      in the left subtree or left child itself.

   b) If the right child of the inorder predecessor is NULL:
      1) Set current as the right child of its inorder 
         predecessor(Making threads between nodes).
      2) Move current node to its left child.

   c) Else, if the threaded link between the current node 
      and it's inorder predecessor already exists :

      1) Set right pointer of the inorder predecessor node as NULL.

      2) If the absolute difference between current's data and 
         the key is smaller than diff:
        a) Set diff variable as the absolute difference between 
           the current node and the key.
        b) Set closest as the current node. 

      3) Move current to its right child.

5)By the time we have traversed the whole tree, we have the 
  closest node, so we simply return closest.

下面是上述方法的实现:

C++
// CPP program to find closest value in
// a Binary Search Tree.
#include 
#include 
using namespace std;
  
// Tree Node
struct Node {
    int data;
    Node *left, *right;
};
  
// Utility function to create a new Node
Node* newNode(int data)
{
    Node* temp = new Node();
    temp->data = data;
    temp->left = temp->right = NULL;
    return temp;
}
  
// Function to find the Node closest to the 
// given key in BST using Morris Traversal
Node* closestNodeUsingMorrisTraversal(Node* root, 
                                         int key)
{
    int diff = INT_MAX;
    Node* curr = root;
    Node* closest;
  
    while (curr) {
        if (curr->left == NULL) {
  
            // updating diff if the current diff is
            // smaller than prev difference
            if (diff > abs(curr->data - key)) {
                diff = abs(curr->data - key);
                closest = curr;
            }
  
            curr = curr->right;
        }
  
        else {
  
            // finding the inorder predecessor
            Node* pre = curr->left;
            while (pre->right != NULL &&
                   pre->right != curr)
                pre = pre->right;
  
            if (pre->right == NULL) {
                pre->right = curr;
                curr = curr->left;
            }
  
            // threaded link between curr and
            // its predecessor already exists
            else {
                pre->right = NULL;
  
                // if a closer Node found, then update 
                // the diff and set closest to current
                if (diff > abs(curr->data - key)) {
                    diff = abs(curr->data - key);
                    closest = curr;
                }
  
                // moving to the right child
                curr = curr->right;
            }
        }
    }
  
    return closest;
}
  
// Driver Code
int main()
{
    /* Constructed binary tree is
          5
        /   \
       3     9
     /  \   /  \
    1    2  8    12 */
    Node* root = newNode(5);
    root->left = newNode(3);
    root->right = newNode(9);
    root->left->left = newNode(1);
    root->left->right = newNode(2);
    root->right->left = newNode(8);
    root->right->right = newNode(12);
  
    cout << closestNodeUsingMorrisTraversal(root, 10)->data;
  
    return 0;
}


Java
// Java program to find closest value in
// a Binary Search Tree.
class GFG 
{
  
  
// Tree Node
static class Node 
{
    int data;
    Node left, right;
};
  
// Utility function to create a new Node
static Node newNode(int data)
{
    Node temp = new Node();
    temp.data = data;
    temp.left = temp.right = null;
    return temp;
}
  
// Function to find the Node closest to the 
// given key in BST using Morris Traversal
static Node closestNodeUsingMorrisTraversal(Node root, 
                                        int key)
{
    int diff = Integer.MAX_VALUE;
    Node curr = root;
    Node closest = null;
  
    while (curr != null) 
    {
        if (curr.left == null) 
        {
  
            // updating diff if the current diff is
            // smaller than prev difference
            if (diff > Math.abs(curr.data - key)) 
            {
                diff = Math.abs(curr.data - key);
                closest = curr;
            }
  
            curr = curr.right;
        }
  
        else
        {
  
            // finding the inorder predecessor
            Node pre = curr.left;
            while (pre.right != null &&
                pre.right != curr)
                pre = pre.right;
  
            if (pre.right == null) 
            {
                pre.right = curr;
                curr = curr.left;
            }
  
            // threaded link between curr and
            // its predecessor already exists
            else
            {
                pre.right = null;
  
                // if a closer Node found, then update 
                // the diff and set closest to current
                if (diff > Math.abs(curr.data - key)) 
                {
                    diff = Math.abs(curr.data - key);
                    closest = curr;
                }
  
                // moving to the right child
                curr = curr.right;
            }
        }
    }
  
    return closest;
}
  
// Driver Code
public static void main(String[] args) 
{
    /* Constructed binary tree is
        5
        / \
    3     9
    / \ / \
    1 2 8 12 */
    Node root = newNode(5);
    root.left = newNode(3);
    root.right = newNode(9);
    root.left.left = newNode(1);
    root.left.right = newNode(2);
    root.right.left = newNode(8);
    root.right.right = newNode(12);
  
    System.out.println(closestNodeUsingMorrisTraversal(root, 10).data);
}
}
  
// This code is contributed by Rajput-Ji


Python3
# Python program to find closest value in
# Binary search Tree
  
_MIN = -2147483648
_MAX = 2147483648
  
# Helper function that allocates a new 
# node with the given data and None left 
# and right poers.                                 
class newNode: 
  
    # Constructor to create a new node 
    def __init__(self, data): 
        self.data = data 
        self.left = None
        self.right = None
  
# Function to find the Node closest to the 
# given key in BST using Morris Traversal
def closestNodeUsingMorrisTraversal(root,key):
    diff = _MAX
    curr = root
    closest=0
  
    while (curr) :
        if (curr.left == None) :
  
            # updating diff if the current diff is
            # smaller than prev difference
            if (diff > abs(curr.data - key)) :
                diff = abs(curr.data - key)
                closest = curr
              
            curr = curr.right
          
  
        else :
  
            # finding the inorder predecessor
            pre = curr.left
            while (pre.right != None and
                    pre.right != curr):
                pre = pre.right
  
            if (pre.right == None): 
                pre.right = curr
                curr = curr.left
              
  
            # threaded link between curr and
            # its predecessor already exists
            else :
                pre.right = None
  
                # if a closer Node found, then update 
                # the diff and set closest to current
                if (diff > abs(curr.data - key)) :
                    diff = abs(curr.data - key)
                    closest = curr
                  
                # moving to the right child
                curr = curr.right
                  
    return closest
  
          
# Driver Code 
if __name__ == '__main__':
    """ /* Constructed binary tree is
        5
        / \
    3 9
    / \ / \
    1 2 8 12 */ """
      
    root = newNode(5) 
    root.left = newNode(3) 
    root.right = newNode(9) 
    root.left.right = newNode(2)
    root.left.left = newNode(1)
    root.right.right = newNode(12)
    root.right.left = newNode(8)
    print(closestNodeUsingMorrisTraversal(root, 10).data)
  
# This code is contributed
# Shubham Singh(SHUBHAMSINGH10)


C#
// C# program to find closest value in
// a Binary Search Tree.
using System;
      
class GFG 
{
  
  
// Tree Node
public class Node 
{
    public int data;
    public Node left, right;
};
  
// Utility function to create a new Node
static Node newNode(int data)
{
    Node temp = new Node();
    temp.data = data;
    temp.left = temp.right = null;
    return temp;
}
  
// Function to find the Node closest to the 
// given key in BST using Morris Traversal
static Node closestNodeUsingMorrisTraversal(Node root, 
                                        int key)
{
    int diff = int.MaxValue;
    Node curr = root;
    Node closest = null;
  
    while (curr != null) 
    {
        if (curr.left == null) 
        {
  
            // updating diff if the current diff is
            // smaller than prev difference
            if (diff > Math.Abs(curr.data - key)) 
            {
                diff = Math.Abs(curr.data - key);
                closest = curr;
            }
  
            curr = curr.right;
        }
  
        else
        {
  
            // finding the inorder predecessor
            Node pre = curr.left;
            while (pre.right != null &&
                pre.right != curr)
                pre = pre.right;
  
            if (pre.right == null) 
            {
                pre.right = curr;
                curr = curr.left;
            }
  
            // threaded link between curr and
            // its predecessor already exists
            else
            {
                pre.right = null;
  
                // if a closer Node found, then update 
                // the diff and set closest to current
                if (diff > Math.Abs(curr.data - key)) 
                {
                    diff = Math.Abs(curr.data - key);
                    closest = curr;
                }
  
                // moving to the right child
                curr = curr.right;
            }
        }
    }
  
    return closest;
}
  
// Driver Code
public static void Main(String[] args) 
{
    /* Constructed binary tree is
        5
        / \
    3     9
    / \ / \
    1 2 8 12 */
    Node root = newNode(5);
    root.left = newNode(3);
    root.right = newNode(9);
    root.left.left = newNode(1);
    root.left.right = newNode(2);
    root.right.left = newNode(8);
    root.right.right = newNode(12);
  
    Console.WriteLine(closestNodeUsingMorrisTraversal(root, 10).data);
}
}
  
/* This code is contributed by PrinciRaj1992 */


输出:
9

时间复杂度:O(n)
辅助空间:O(1)