📜  K维树|套装3(删除)

📅  最后修改于: 2021-04-17 09:10:39             🧑  作者: Mango

我们强烈建议您参考以下帖子,以此作为前提条件。

K维树|设置1(搜索和插入)
K维树|第2组(查找最小值)

在这篇文章中,讨论了删除。该操作是从KD树中删除给定点。

像二进制搜索树删除一样,我们递归遍历并搜索要删除的点。以下是访问每个节点的步骤。

1)如果当前节点包含要删除的点

  1. 如果要删除的节点是叶节点,则只需将其删除(与BST删除相同)
  2. 如果要删除的节点的右子节点不为NULL(与BST不同)
    1. 在右侧子树中找到当前节点尺寸的最小值。
    2. 将节点替换为上面找到的最小值,然后递归删除右侧子树中的最小值。
  3. 否则,如果要删除的节点的子节点不为NULL(与BST不同)
    1. 在左侧子树中找到当前节点尺寸的最小值。
    2. 将节点替换为上面找到的最小值,然后递归删除左侧子树中的最小值。
    3. 将新的左子树作为当前节点的右子节点。

2)如果当前不包含要删除的点

  1. 如果要删除的节点小于当前维度上的当前节点,请为左子树重复。
  2. 否则,再重新选择正确的子树。

为什么1.b和1.c与BST不同?
在BST删除中,如果节点的左子节点为空而右节点不为空,则将节点替换为右子节点。在KD树中,这样做会违反KD树属性,因为节点的右子节点的尺寸与节点的尺寸不同。例如,如果节点将点除以x轴值。然后将其子项除以y轴,因此我们不能简单地用正确的子项替换node。对于右边的孩子不为空而左边的孩子为空的情况也是如此。

为什么1.c在左子树中找不到max,然后像1.b那样为max递归?
这样做违反了所有相等值都在正确的子树中的属性。例如,如果我们删除下面子树中的(!0,10)并替换为

Wrong Way (Equal key in left subtree after deletion)
            (5, 6)                             (4, 10)
             /              Delete(5, 6)         /  
        (4, 10)            ------------>     (4, 20)
             \
           (4, 20) 

Right way (Equal key in right subtree after deletion)
             (5, 6)                          (4, 10)
             /              Delete(5, 6)           \
         (4, 10)            ------------>         (4, 20)
              \
             (4, 20) 

删除示例:
删除(30,40):由于右子节点不是NULL,并且节点的维数是x,因此我们在右子节点中找到x值最小的节点。节点为(35,45),我们将(30,40)替换为(35,45)并删除(30,40)。

kdtreedelete2

删除(70,70):节点的尺寸为y。由于右子节点为NULL,因此我们在左子节点中找到y值最小的节点。节点为(50,30),我们将(70,70)替换为(50,30),然后递归删除左侧子树中的(50,30)。最后,我们将修改后的左子树作为(50,30)的右子树。
kdtreedelete

以下是KD树删除的C++实现。

// A C++ program to demonstrate delete in K D tree
#include
using namespace std;
  
const int k = 2;
  
// A structure to represent node of kd tree
struct Node
{
    int point[k]; // To store k dimensional point
    Node *left, *right;
};
  
// A method to create a node of K D tree
struct Node* newNode(int arr[])
{
    struct Node* temp = new Node;
  
    for (int i=0; ipoint[i] = arr[i];
  
    temp->left = temp->right = NULL;
    return temp;
}
  
// Inserts a new node and returns root of modified tree
// The parameter depth is used to decide axis of comparison
Node *insertRec(Node *root, int point[], unsigned depth)
{
    // Tree is empty?
    if (root == NULL)
        return newNode(point);
  
    // Calculate current dimension (cd) of comparison
    unsigned cd = depth % k;
  
    // Compare the new point with root on current dimension 'cd'
    // and decide the left or right subtree
    if (point[cd] < (root->point[cd]))
        root->left = insertRec(root->left, point, depth + 1);
    else
        root->right = insertRec(root->right, point, depth + 1);
  
    return root;
}
  
// Function to insert a new point with given point in
// KD Tree and return new root. It mainly uses above recursive
// function "insertRec()"
Node* insert(Node *root, int point[])
{
    return insertRec(root, point, 0);
}
  
// A utility function to find minimum of three integers
Node *minNode(Node *x, Node *y, Node *z, int d)
{
    Node *res = x;
    if (y != NULL && y->point[d] < res->point[d])
       res = y;
    if (z != NULL && z->point[d] < res->point[d])
       res = z;
    return res;
}
  
// Recursively finds minimum of d'th dimension in KD tree
// The parameter depth is used to determine current axis.
Node *findMinRec(Node* root, int d, unsigned depth)
{
    // Base cases
    if (root == NULL)
        return NULL;
  
    // Current dimension is computed using current depth and total
    // dimensions (k)
    unsigned cd = depth % k;
  
    // Compare point with root with respect to cd (Current dimension)
    if (cd == d)
    {
        if (root->left == NULL)
            return root;
        return findMinRec(root->left, d, depth+1);
    }
  
    // If current dimension is different then minimum can be anywhere
    // in this subtree
    return minNode(root,
               findMinRec(root->left, d, depth+1),
               findMinRec(root->right, d, depth+1), d);
}
  
// A wrapper over findMinRec(). Returns minimum of d'th dimension
Node *findMin(Node* root, int d)
{
    // Pass current level or depth as 0
    return findMinRec(root, d, 0);
}
  
// A utility method to determine if two Points are same
// in K Dimensional space
bool arePointsSame(int point1[], int point2[])
{
    // Compare individual pointinate values
    for (int i = 0; i < k; ++i)
        if (point1[i] != point2[i])
            return false;
  
    return true;
}
  
// Copies point p2 to p1
void copyPoint(int p1[], int p2[])
{
   for (int i=0; ipoint, point))
    {
        // 2.b) If right child is not NULL
        if (root->right != NULL)
        {
            // Find minimum of root's dimension in right subtree
            Node *min = findMin(root->right, cd);
  
            // Copy the minimum to root
            copyPoint(root->point, min->point);
  
            // Recursively delete the minimum
            root->right = deleteNodeRec(root->right, min->point, depth+1);
        }
        else if (root->left != NULL) // same as above
        {
            Node *min = findMin(root->left, cd);
            copyPoint(root->point, min->point);
            root->right = deleteNodeRec(root->left, min->point, depth+1);
        }
        else // If node to be deleted is leaf node
        {
            delete root;
            return NULL;
        }
        return root;
    }
  
    // 2) If current node doesn't contain point, search downward
    if (point[cd] < root->point[cd])
        root->left = deleteNodeRec(root->left, point, depth+1);
    else
        root->right = deleteNodeRec(root->right, point, depth+1);
    return root;
}
  
// Function to delete a given point from K D Tree with 'root'
 Node* deleteNode(Node *root, int point[])
{
   // Pass depth as 0
   return deleteNodeRec(root, point, 0);
}
  
// Driver program to test above functions
int main()
{
    struct Node *root = NULL;
    int points[][k] = {{30, 40}, {5, 25}, {70, 70},
                      {10, 12}, {50, 30}, {35, 45}};
  
    int n = sizeof(points)/sizeof(points[0]);
  
    for (int i=0; ipoint[0] << ", " << root->point[1] << endl;
  
    return 0;
}

输出:

Root after deletion of (30, 40)
35, 45

来源:
https://www.cs.umd.edu/class/spring2008/cmsc420/L19.kd-trees.pdf