简体   繁体   English

使用父节点创建二叉搜索树节点

[英]Creating a Binary Search Tree node with Parent node

I have a BST defined as follows:我有一个 BST 定义如下:

typedef struct trnode {
    Item item;
    struct trnode *left;
    struct trnode *right;
} Trnode;

typedef struct tree {
    Trnode *root;
    size_t size;
} Tree;

The issue I'm having is often I want to know what the parent of a particular tree node is.我遇到的问题通常是我想知道特定树节点的父节点是什么。 Is it common at all to define a tree node which includes the parent, such as:定义一个包含父节点的树节点是否很常见,例如:

typedef struct trnode {
    Item item;
    struct trnode *parent;
    struct trnode *left;
    struct trnode *right;
} Trnode;

Or is including the parent something that should not be done, and if so: why not?或者是否包括父母不应该做的事情,如果是这样:为什么不呢?


Update: someone has requested to see my deletion code.更新:有人要求查看我的删除代码。 This is untested and was pretty difficult for me to write (I'm a beginniner in C and also just learning the BST data structure).这是未经测试的,我很难写(我是 C 的初学者,也只是学习 BST 数据结构)。 Anyways, here it is:无论如何,这里是:

Node * SeekInsertionParent(const Node *root, const Node *pnode)
{
    // cmp returns -1 (less than), +1 (greater than), or 0 (equal)
    int cmp = CmpNodes(pnode, root);
    if (cmp == 0) return NULL; // ignore this for now
    Node *child = (cmp < 0)? root->left : root->right;
    if (child == NULL)
        return root;
    else
        SeekInsertionParent(child, pnode);
}
Bool DeleteItem(Tree *ptree, const Item *pi)
{
    Node *parent;
    Node *node = SeekItem(pi, ptree);
    if (node == NULL)
        return false;

    // (1) if no children, then it's a leaf, just delete it
    if (!node->left && !node->right) {
        free(node);
        return true;
    }

    // (2) if it has one child, link that child to the parent then free the node
    if (!node->left || !node->right) {
        Node *descendant = (node->left)? node->left : node->right;
        descendant->parent = parent;
        if (parent->left == node)
            parent->left = descendant;
        else
            parent->right = descendant;
        free(node);
    }

    // (3) if it has two children, then:
    //     (a) attach the child same-side child to the parent node;
    //     (b) using the root of the attachment, find the place to insert the other-side child
    else {
        Node *insert_at, *other_side;
        if (parent->left == node) {
            node->left->parent = parent;
            parent->left = node->left;
            other_side = node->right;
        } else {
            node->right->parent = parent;
            parent->right = node->right;
            other_side = node->left;
        }

        free(node);
        insert_at = SeekInsertionParent(parent, other_node);

        if (insert_at->left == NULL) {
            insert_at->left=node;
            node->parent=insert_at;
        } else {
            insert_at->right=node;
            node->parent=insert_at;
        }
    }
    return true;
}

By adding the parent you will add O(n) memory which is not what you want to do as most of the time your algo will run in O(logN).通过添加父级,您将添加 O(n) memory,这不是您想要做的,因为大多数时候您的算法将在 O(logN) 中运行。

If you really want to implement it, you can simply find the model of double LinkedList and replicate this to build BST with parent.如果你真的想实现它,你可以简单地找到双 LinkedList的 model 并将其复制到与父级一起构建 BST。

Note that you could take inspiration from XOR Linkedlist to potentially solve the memory surplus:请注意,您可以从XOR Linkedlist中获取灵感,以潜在地解决 memory 盈余问题:

Trnode(current) = Trnode(parent)^Trnode(current->left ^ current->left)
Trnode(current->left) = Trnode(current)^Trnode(current->left->left^current->left->right)

It is really worth it, especially if you do not need to alter the tree:这真的很值得,特别是如果您不需要更改树:

  • to delete a Trnode from the tree knowing only its address or从只知道其地址的树中删除一个 Trnode 或
  • to insert a new node before or after an existing node when knowing only the address of the existing node.当只知道现有节点的地址时,在现有节点之前或之后插入一个新节点。

As promised, the simplified version, using a pointer-to-pointer.正如所承诺的,简化版本,使用指针指向。

The point is: you do not need to maintain the parent pointer, since it can always be computed.关键是:您不需要维护父指针,因为它总是可以计算的。 I created two helper functions to obtain a pointer to the pointer that points to a given node (or value).我创建了两个辅助函数来获取指向给定节点(或值)的指针的指针 The same helper functions can be used in an insert-function.插入函数中可以使用相同的辅助函数。


#if 0
#include <stdlib.h>
#include <stdbool.h>
#define Bool bool
#else
typedef enum bool{ false, true} Bool;
void free(void*);
#define NULL (void*) 0
#endif

typedef struct node {
    int item;
    struct node *left;
    struct node *right;
} Node;


        // Helper function to find the pointer that points to the node containing item
static Node **node_seek_parent_pp_value(Node **pp, int item)
{
    // cmp returns -1 (less than), +1 (greater than), or 0 (equal)
    while (*pp) {
        int cmp;
        cmp = (*pp)->item == item ? 0 : (*pp)->item < item ? 1 : -1;
        if (cmp == 0) break; // Found!
        pp = (cmp < 0)? &(*pp)->left : &(*pp)->right;
        }

   return pp;
}

        // Same helper function, but with a pointer to item argument
static Node **node_seek_parent_pp_ptr(Node **pp, Node *pnode)
{
if (!pnode) return NULL;
return node_seek_parent_pp_value(pp, pnode->item);
}

Bool DeleteItem(Node **pp, int item)
{
    Node *del;

    pp = node_seek_parent_pp_value(pp, item);
    if (!pp || !*pp) return false; // Not found

    // (1) if fewer than two children
    if (!(*pp)->left || !(*pp)->right) {
        del = *pp;
        *pp = del->left ? del->left : del->right;
    }

    // (2) if it has two children, then:
    //     (a) detach one child subtree
    //     (b) insert it onto the other child
    //     (c) put this other child in place of the node to be deleted.
    else {
        Node **target, *orphan;
        del = *pp;
        orphan = del->right;
        target = node_seek_parent_pp_ptr(&del->left, orphan);
        if (!target) { // should not happen ...
            return false;
            }
        *target = orphan;
        *pp = del->left;
        }

    free(del);
    return true;
}

Bool InsertNode(Node **pp, Node * this)
{
pp = node_seek_parent_pp_ptr(pp, this);
if (!pp || *pp) return false; // this is NULL, or already existing
*pp = this; // insert
return true; // Success!
}

You may want to read this to understand how deletion is done without using parent pointer in node representation.您可能想阅读 本文以了解如何在不使用节点表示中的parent指针的情况下完成删除。 In CLRS , it' givenCLRS中,它给出了

We can represent such a binary search tree by a linked data structure in which each node is an object.我们可以用一个链接数据结构来表示这样一个二叉搜索树,其中每个节点都是一个 object。 In addition to a key and satellite data, each node contains attributes left , right , and p that point to the nodes corresponding to its left child, its right child, and its parent, respectively.除了键和卫星数据之外,每个节点还包含属性leftrightp ,它们分别指向与其左孩子、右孩子和父节点对应的节点。 If a child or the parent is missing, the appropriate attribute contains the value NIL .如果缺少子项或父项,则相应的属性包含值NIL The root node is the only node in the tree whose parent is NIL根节点是树中唯一其父节点为NIL的节点

They use parent pointer since it facilitates to implement procedures like delete , find-next-larger , find-next-smaller , etc. It's not a problem to use it in your code but, it costs O(n) extra memory space.他们使用parent指针,因为它有助于实现诸如deletefind-next-largerfind-next-smaller等过程。在代码中使用它不是问题,但是会花费O(n)额外的 memory 空间。 Keep in mind if you don't use parent pointer in the node representation, then you need to implement separate function that takes as argument tree_node and returns parent of tree_node , when you need procedures that requires parent of some nodes.请记住,如果您不在节点表示中使用parent指针,那么当您需要需要某些节点的父级的过程时,您需要实现单独的 function 作为参数tree_node并返回tree_node的父级。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM