二叉搜索树的节点删除(c实现)

BST节点删除的情况可以细分为6种情况

  1. 根节点的删除
    1.1 根节点没有左右孩子
    1.2 根节点只有左孩子
    1.3 根节点只有右孩子
    1.4 根节点有左右孩子
  2. 子节点的删除
    2.1 子节点没有左右孩子
    2.2 子节点只有左孩子
    2.3 子节点只有右孩子
    2.4 子节点有左右孩子

现在配合图例来分别描述这六种情况.

创建后的BST结构如下:


BST.png

1 根节点的删除

1.1 根节点没有左右孩子
很好理解,只有一个根节点,删除就好了
1.2 根节点只有左孩子
这种情况就是把根节点删除,把左孩子节点当做新的根节点

001.png

1.3 根节点只有右孩子同1.2
1.4 根节点有左右孩子(这里只考虑操作左子树的情况,右子树同理)
根据BST的特性,左子树的右叶子<根节点<右子树的左叶子,所以当删除根节点的时候,确保BST的特性,可以将
左子树的右叶子或者右子树的左叶子的值来代替根节点,并将该节点删除.
1.4.1 根节点的左孩子有左右子树
002.png

1.4.2 根节点的左孩子只有右子树(同1.4.1)
1.4.3 根节点的左孩子只有左子树
003.png

1.4.4 根节点的左右孩子都没有孩子(这里只考虑左子树的操作)
004.png

2 子节点的删除

2.1 子节点没有左右孩子
删除节点既可
2.2 子节点只有左孩子
这种情况与1.4.3的情况类似,只是不用交换节点值,将子节点指向子节点的左孩子既可
2.3 子节点只有右孩子
这种情况与2.3相同.只是操作右节点
2.4 子节点有左右孩子
2.4.1 要删除的子节点在根节点的左边
与1.4.1的请相同,例如我们如果要删除5节点


004.png

006.png

2.4.2 要删除的子节点在根节点的右边
与上面相同,只是操作右子树
2.4.3 要删除的子节点有左右两个孩子,但是左孩子没有右孩子
这种情况不满足2.4.1或者2.4.2的条件,需要特别考虑


007.png
008.png
#include<stdio.h>
#include<stdlib.h>

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

Node *newNode(int data);
Node *insert(Node *node, int data);
Node *getParent(Node *node, Node *parent, int target);
void printPreorder(Node *node);
void printInorder(Node *node);
void printPostorder(Node *node);
Node *lookup(Node *node, int target);
Node *leftLeaf(Node *node);
Node *rightLeaf(Node *node);
Node *delNode(Node *root, int target);

int main(void) {
    Node *root = newNode(10);
    /* following is the tree after above statement
            10
          /   \
         NULL  NULL
    */
    insert(root, 5);
    insert(root, 15);
    /* 2 and 3 become left and right children of 1
               10
             /   \
            5      15
         /    \    /  \
        NULL NULL NULL NULL
    */
    insert(root, 3);
    /* 4 becomes left child of 2
                  10
               /      \
              5        15
            /   \     /  \
           3   NULL NULL  NULL
          /  \
        NULL NULL
    */
    insert(root, 7);
    insert(root, 8);
    insert(root, 6);
    // insert(root, 4);
    insert(root, 2);
    insert(root, 20);
    /* 4 insert some node
                 10
               /    \
              5      15
            /   \      \
           3     7     20
          / \   / \
         2   4  6  8
    */
    delNode(root, 5);
    printf("\n");
    printf("preorder:");
    printPreorder(root);//前序遍历:10--5--3--2--4--7--6--8--20
    free(root);
    return 0;
}

//创建新节点
Node *newNode(int data) {
    Node *node = (Node *)malloc(sizeof(Node));
    node->data = data;
    node->left = NULL;
    node->right = NULL;
    return node;
};

//插入节点
Node *insert(Node *node, int data) {
    // 1. If the tree is empty, return a new, single node
    if(node == NULL) {
        return newNode(data);
    }
    // 2. Otherwise, recur down the tree
    if(data < node->data) {
        node->left = insert(node->left, data);
    } else {
        node->right = insert(node->right, data);
    }
    //return the (unchanged) node pointer
    return node;
}

//获取指定值的父亲节点
Node *getParent(Node *node, Node *parent, int target) {
    if(node == NULL) {
        return NULL;
    }
    if(target == node->data) {
        return parent;
    }
    parent = node;
    if(target < node->data) {
        return getParent(node->left, parent, target);
    } else {
        return getParent(node->right, parent, target);
    }
}

//前序遍历
void printPreorder(Node *node) {
    if(node == NULL) {
        return;
    }
    printf("%d--", node->data);
    printPreorder(node->left);
    printPreorder(node->right);
}

//中序遍历
void printInorder(Node *node) {
    if(node == NULL) {
        return;
    }
    printInorder(node->left);
    printf("%d--", node->data);
    printInorder(node->right);
}

//后序遍历
void printPostorder(Node *node) {
    if (node == NULL) return;
    // first recur on both subtrees
    printPostorder(node->left);
    printPostorder(node->right);
    // then deal with the node
    printf("%d--", node->data);
}

//左叶子节点
Node *leftLeaf(Node *node) {
    if(node->left == NULL) {
        return node;
    }
    return leftLeaf(node->left);
}

//右叶子节点
Node *rightLeaf(Node *node) {
    if(node->right == NULL) {
        return node;
    }
    return rightLeaf(node->right);
}

//查找指定值的节点
Node *lookup(Node *node, int target) {
    if(node == NULL) {
        return NULL;
    }
    if(target == node->data) {
        return node;
    }
    if(target < node->data) {
        return lookup(node->left, target);
    } else {
        return lookup(node->right, target);
    }
}

//删除指定值的节点
Node *delNode(Node *root, int target) {
    Node *node = lookup(root, target);
    Node *ret = NULL;
    if(node == NULL) {
        return ret;
    }
    Node *parent = getParent(root, NULL, target);
    //根节点的情况
    if(parent == NULL) {
        //没有左右孩子
        if(node->left == NULL && node->right == NULL) {
            ret = NULL;
        }
        //只有左孩子
        if(node->left && node->right == NULL) {
            ret = node->left;
        }
        //只有右孩子
        if(node->right && node->left == NULL) {
            ret = node->right;
        }
        //有左右孩子
        if(node->left && node->right) {
            //左子树的最右边叶子节点
            Node *leaf = rightLeaf(node->left);
            //node->left没有右子树
            if(leaf == node->left) {
                node->data = leaf->data;
                node->left = leaf->left;
            } else {
                //找到叶子节点的父亲
                Node *leafParent = getParent(root, NULL, leaf->data);
                //交换叶子节点的值
                node->data = leaf->data;
                //叶子节点父亲就是操作节点
                if(leafParent == node) {
                    //删除左子树
                    leafParent->left = NULL;
                } else {
                    //删除右子树
                    leafParent->right = NULL;
                }
            }
            ret = node;
        }
    } else {
        //非根节点的情况
        //没有左右孩子
        if(node->left == NULL && node->right == NULL) {
            //节点值>操作节点值,说明是父节点的右孩子.反之是父节点的左孩子
            if(node->data > parent->data) {
                parent->right = NULL;
            } else {
                parent->left = NULL;
            }
            ret = parent;
        }
        //只有左子树
        if(node->left && node->right == NULL) {
            //节点值>操作节点值,说明是父节点的右孩子.反之是父节点的左孩子
            if(node->data > parent->data) {
                parent->right = node->left;
            } else {
                parent->left = node->left;
            }
            ret = parent;
        }
        //只有右子树
        if(node->right && node->left == NULL) {
            //节点值>操作节点值,说明是父节点的右孩子.反之是父节点的左孩子
            if(node->data > parent->data) {
                parent->right = node->right;
            } else {
                parent->left = node->right;
            }
            ret = parent;
        }
        //有左右子树
        if(node->left && node->right) {
            //节点值>操作节点值,说明是父节点的右孩子.反之是父节点的左孩子
            if(node->data < parent->data) {
                Node *leaf = rightLeaf(node->left);
                Node *leafParent = getParent(root, NULL, leaf->data);
                node->data = leaf->data;
                //父亲节点就是操作节点
                if(leafParent == node) {
                    leafParent->left = leaf->left;
                } else {
                    leafParent->right = NULL;
                }
            } else {
                Node *leaf = leftLeaf(node->right);
                Node *leafParent = getParent(root, NULL, leaf->data);
                node->data = leaf->data;
                //父亲节点就是操作节点
                if(leafParent == node) {
                    leafParent->right = leaf->right;
                } else {
                    leafParent->left = NULL;
                }
            }
            ret = node;
        }
    }
    return ret;
}

上面细分了6种删除节点的情况,可以总结成3种情况
1.删除节点是叶子节点
2.删除节点有左/右子树
3.删除节点有左右子树(相当于找左子树的最大值或者右子树的最小值)

//找到左子树的最大值
Node* findMax(Node* node){
    return node->right ? findMax(node->right) : node;
}

//找到右子树的最小值
Node* findMin(Node* node){
    return node->left ? findMax(node->left) : node;
}
Node *delNode3(Node *tree, Node *target) {
    if(tree == NULL || target == NULL) {
        return NULL;
    }
    //要删除的节点在左子树,递归删除
    if(target->data < tree->data) {
        tree->left = delNode3(tree->left, target);
    //要删除的节点在右子树,递归删除
    } else if(target->data > tree->data) {
        tree->right = delNode3(tree->right, target);
    } else {
        //1.待删除的节点有左右子树
        if(tree->left && tree->right) {
            //找到左子树的最大值节点或者找到右子树的最小值节点
            Node *temp = findMax(tree->left);
            // Node *temp = findMin(tree->right);
            //交换值
            tree->data = temp->data;
            //重建左子树
            tree->left = delNode3(tree->left, temp);
            free(temp);
        } else {
            //2.待删除的节点有左/右子树
            if(tree->left == NULL) {
                tree = tree->right;
            } else if(tree->right == NULL) {
                tree = tree->left;
            } else {
                //3.待删除的节点没有左右子树
                tree = NULL;
            }
        }
    }
    return tree;
}

进行测试打印输出是否是自己需要的结果

    /* 
                 10
               /    \
              5      15
            /   \      \
           3     7     20
          / \   / \
         2   4  6  8
    */
Node *t = lookup(root,10);
Node *ret = delNode3(root, t);
printPreorder(ret);//preorder:8--5--3--2--4--7--6--15--20

Node *t = lookup(root,2);
Node *ret = delNode3(root, t);
printPreorder(ret);//preorder:10--5--3--4--7--6--8--15--20

Node *t = lookup(root,5);
Node *ret = delNode3(root, t);
printPreorder(ret);//preorder:10--4--3--2--7--6--8--15--20
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,743评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,296评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,285评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,485评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,581评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,821评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,960评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,719评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,186评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,516评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,650评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,329评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,936评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,757评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,991评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,370评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,527评论 2 349

推荐阅读更多精彩内容