簡體   English   中英

使用父節點創建二叉搜索樹節點

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

我有一個 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;
}

通過添加父級,您將添加 O(n) memory,這不是您想要做的,因為大多數時候您的算法將在 O(logN) 中運行。

如果你真的想實現它,你可以簡單地找到雙 LinkedList的 model 並將其復制到與父級一起構建 BST。

請注意,您可以從XOR Linkedlist中獲取靈感,以潛在地解決 memory 盈余問題:

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

這真的很值得,特別是如果您不需要更改樹:

  • 從只知道其地址的樹中刪除一個 Trnode 或
  • 當只知道現有節點的地址時,在現有節點之前或之后插入一個新節點。

正如所承諾的,簡化版本,使用指針指向。

關鍵是:您不需要維護父指針,因為它總是可以計算的。 我創建了兩個輔助函數來獲取指向給定節點(或值)的指針的指針 插入函數中可以使用相同的輔助函數。


#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!
}

您可能想閱讀 本文以了解如何在不使用節點表示中的parent指針的情況下完成刪除。 CLRS中,它給出了

我們可以用一個鏈接數據結構來表示這樣一個二叉搜索樹,其中每個節點都是一個 object。 除了鍵和衛星數據之外,每個節點還包含屬性leftrightp ,它們分別指向與其左孩子、右孩子和父節點對應的節點。 如果缺少子項或父項,則相應的屬性包含值NIL 根節點是樹中唯一其父節點為NIL的節點

他們使用parent指針,因為它有助於實現諸如deletefind-next-largerfind-next-smaller等過程。在代碼中使用它不是問題,但是會花費O(n)額外的 memory 空間。 請記住,如果您不在節點表示中使用parent指針,那么當您需要需要某些節點的父級的過程時,您需要實現單獨的 function 作為參數tree_node並返回tree_node的父級。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM