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

C 使用父节点创建二叉搜索树节点,c,binary-search-tree,C,Binary Search Tree,我的BST定义如下: typedef struct trnode { Item item; struct trnode *left; struct trnode *right; } Trnode; typedef struct tree { Trnode *root; size_t size; } Tree; 我经常遇到的问题是,我想知道特定树节点的父节点是什么。定义包含父节点的树节点是否很常见,例如: typedef struct trnode {

我的BST定义如下:

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

typedef struct tree {
    Trnode *root;
    size_t size;
} Tree;
我经常遇到的问题是,我想知道特定树节点的父节点是什么。定义包含父节点的树节点是否很常见,例如:

typedef struct trnode {
    Item item;
    struct trnode *parent;
    struct trnode *left;
    struct trnode *right;
} Trnode;
或者,包括父母是不应该做的事情,如果是,为什么不


更新:有人请求查看我的删除代码。这是未经测试的,对我来说编写起来相当困难(我是C语言的初学者,也只是学习BST数据结构)。不管怎样,这是:

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;
}
Node*SeekInsertionParent(常量节点*根,常量节点*pnode)
{
//cmp返回值-1(小于)、+1(大于)或0(等于)
int cmp=CmpNodes(pnode,root);
if(cmp==0)返回NULL;//暂时忽略此选项
节点*child=(cmp<0)?根->左:根->右;
if(child==NULL)
返回根;
其他的
参见亲子关系父项(子项,pnode);
}
Bool DeleteItem(树*ptree,常量项*pi)
{
节点*父节点;
Node*Node=SeekItem(pi,ptree);
if(node==NULL)
返回false;
//(1)如果没有子项,则它是一片叶子,只需删除它即可
如果(!node->left&&!node->right){
自由(节点);
返回true;
}
//(2)如果它有一个子节点,则将该子节点链接到父节点,然后释放该节点
如果(!节点->左| |!节点->右){
节点*子体=(节点->左)?节点->左:节点->右;
后代->父代=父代;
如果(父->左==节点)
父->左=子代;
其他的
父->右=子代;
自由(节点);
}
//(3)如果它有两个孩子,那么:
//(a)将子节点同侧子节点附加到父节点;
//(b)使用附件的根部,找到插入另一侧儿童的位置
否则{
节点*在*另一侧插入_;
如果(父->左==节点){
节点->左侧->父节点=父节点;
父节点->左=节点->左;
另一侧=节点->右侧;
}否则{
节点->右侧->父节点=父节点;
父->右=节点->右;
另一侧=节点->左侧;
}
自由(节点);
插入\u at=SeekInsertionParent(父节点,其他\u节点);
如果(在->左==NULL处插入_){
在->左=节点插入_;
节点->父节点=插入处;
}否则{
在->右=节点插入_;
节点->父节点=插入处;
}
}
返回true;
}

通过添加父级,您将添加O(n)内存,而这不是您希望执行的操作,因为大部分时间您的算法将在O(logN)中运行

如果您真的想要实现它,您可以简单地找到的模型并复制它以使用父级构建BST

请注意,您可以从中获得灵感来解决内存过剩问题:

Trnode(当前)=Trnode(父节点)^Trnode(当前->左^current->左)
Trnode(当前->左)=Trnode(当前)^Trnode(当前->左->左^current->左->右)
这是非常值得的,特别是如果您不需要更改树:

  • 从树中删除仅知道其地址或
  • 当只知道现有节点的地址时,在现有节点之前或之后插入新节点
您可能需要阅读以了解如何在节点表示中不使用
parent
指针的情况下进行删除。事实上,它是给定的

我们可以用一个链接的数据结构来表示这样一个二叉搜索树,其中 每个节点都是一个对象。除了密钥和卫星数据外,每个节点还包含 属性
left
right
p
,分别指向其左子节点、右子节点和父节点。如果缺少子项或父项,则相应的属性包含值
NIL
。根节点是中的唯一节点 其父级为
NIL


它们使用
parent
指针,因为它有助于实现
delete
查找下一个较大的
查找下一个较小的
等过程。在代码中使用它并不是问题,但它需要
O(n)
额外的内存空间。请记住,如果在节点表示中不使用
parent
指针,则需要实现单独的函数,该函数将
tree\u node
作为参数,并返回
tree\u node
的父节点,当您需要需要某些节点的父节点的过程时。

如承诺的,简化版,使用指向指针的指针

关键是:您不需要维护父指针,因为它总是可以计算的。我创建了两个helper函数来获取指向给定节点(或值)的指针。插入函数中可以使用相同的辅助函数


#如果0
#包括
#包括
#定义布尔布尔布尔
#否则
typedef enum bool{false,true}bool;
无效(无效*);
#定义空(空*)0
#恩迪夫
类型定义结构节点{
国际项目;
结构节点*左;
结构节点*右;
}节点;
//Helper函数查找指向包含项的节点的指针
静态节点**Node\u seek\u parent\u pp\u值(节点**pp,int项)
{
//cmp返回值-1(小于)、+1(大于)或0(等于)
而(*pp){
int-cmp;
cmp=(*pp)->项目==项目0:(*pp)->项目<项目1:-1;
如果(cmp==0)中断;//找到!
pp=(cmp<0)?&(*pp)->左:&(*pp)->右;
}
返回pp;
}
//相同的helper函数,但带有指向项参数的指针
静态节点**Node\u seek\u parent\u pp\u ptr(节点**pp,节点*pnode)
{
如果(!pnode)返回NULL;
返回节点\u seek\u父节点\u pp\u值(pp,pnode->item);
}
Bool DeleteItem(节点**pp,int项)
{
节点*del;
pp=节点搜索父节点pp值(pp,项目);
如果(!pp | |!*pp)返回false;//未找到
//(1)如果少于两个孩子
如果(!(*pp)->左(*pp)->右){
del=*p
#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!
}