二叉查找树

二叉查找树

先说一下什么是二叉查找树,它是一种特殊的二叉树,其递归定义如下:
- 左子树任意节点值小于根节点,右子树任意节点值大于根节点
- 任意节点的左子树和右子树也是二叉查找树
- 任意节点的值不同


二叉查找树的功能主要有三点:
- 插入
- 删除
- 查询

但因为使用二叉查找树进行上述操作,所用时间和树的深度成正比,因此若有N个元素,操作的平均时间复杂度为O(logN),是一种极为高效的数据结构。


二叉查找树如何实现呢?我们先逐一分析。

查询

假设我们要查询数字X,我们先让X和根节点比,如果X小,则X在根节点的左子树中,反之在根节点的右子树中,然后继续按照上述规则与子树的根节点相比较,直到匹配成功。

插入

插入和查询类似,也是先和根节点比较,小就放左边,大就放右边,直到存在空节点并且满足构成的树是二叉查找树。

删除

删除较为复杂,因为一旦把中间某个节点删除,其子树便悬空了,那么我们如何处理这种情况呢?我们首先想到,能不能把子树的某一个节点提到该节点处,使之成为新的二叉查找树呢?这种想法是可行的,不过要遵从我们的目的:使之成为一棵新的二叉查找树。下面直接给出三个处理步骤:
- 如果删除节点没有左儿子,则直接把右儿子提上去。
- 如果删除节点左儿子没有右儿子,则把左儿子提上去。
- 如果不满足上两种情况,则把左儿子子孙中最大的提上去。


那么如何用代码实现呢?

我们使用结构体来表征每一个节点(每个节点拥有数据值以及指向左右儿子的指针)。

typedef struct BST{
    int data;
    struct BST *left,*right;
}node;

根据上述分析,插入元素代码如下:

node *insertData(node *ptr,int data)
{
    if(ptr == NULL){                         //空节点则构造新节点并返回
        node *temp;
        temp = (node *)malloc(sizeof(node));
        temp->data = data;
        temp->left = NULL;          //将左右子树置空,预留位置
        temp->right = NULL;
        return temp;
    }
    else{
        if(data < ptr->data)     //如果小于,放在左子树
            ptr->left = insertData(ptr->left,data);
        else                   //如果大于,放在右子树
            ptr->right = insertData(ptr->right,data);
        return ptr;
    }
}

查询代码如下:

int findData(node *ptr,int data)   //返回1表示存在,返回0表示不存在
{
    if(ptr == NULL)    //如果空,表示没找到,返回0
        return 0;
    else if(ptr->data == data)  //找到,返回1
        return 1;
    else if(data < ptr->data)   //如果小于当前节点值,则从左子树继续找
        return findData(ptr->left,data);
    else    //如果大于,则从右子树继续找
        return findData(ptr->right,data);
}

删除代码如下:

node *deleteData(node *ptr,int data) //在ptr指向节点的子树及自己中删除data
{
    if(ptr == NULL)   //如果空节点,则返回NULL
        return NULL;
    else if(data < ptr->data)  //如果小于,则从左子树继续进行
        ptr->left = deleteData(ptr->left,data);
    else if(data > ptr->data) //如果大于,则从右子树继续进行
        ptr->right = deleteData(ptr->right,data);
    else if(ptr->left == NULL){ //如果没有左子树,则把右子树提上去
       node *temp = ptr->right;
       free(ptr);
       return temp;
    }
    else if(ptr->left->right == NULL){ //如果左儿子没有右儿子,把左儿子提上去
       node *temp = ptr->left;
       temp->right = ptr->right;
       free(ptr);
       return temp; 
    }
    else{                  //否则把左儿子最大子孙提上去
        node *temp;
        for(temp = ptr->left;temp->right->right != NULL;temp = temp->right)
            ;// 找到最大子孙的父亲
        node *temp2 = temp->right;  //temp2为最大子孙
        temp->right = temp2->left;  //最大子孙的父亲接过右儿子的左儿子
        temp2->left = ptr->left;  //最大子孙成功提上去,取代ptr
        temp2->right = ptr->right;
        free(ptr) ;
        return temp2;
    }
    return ptr;
}

现在我们来测试一下,测试代码如下:

#include <iostream>
#include <stdlib.h>
#include <stdio.h>
using namespace std; 

typedef struct BST{
    int data;
    struct BST *left,*right;
}node;

void print(node *root)   //中序遍历 
{
    if(root){
        cout<<root->data<<" ";
        print(root->left);
        print(root->right);
    }
}

node *insertData(node *ptr,int data)
{
    if(ptr == NULL){                         //空节点则构造新节点并返回
        node *temp;
        temp = (node *)malloc(sizeof(node));
        temp->data = data;
        temp->left = NULL;          //将左右子树置空,预留位置
        temp->right = NULL;
        return temp;
    }
    else{
        if(data < ptr->data)     //如果小于,放在左子树
            ptr->left = insertData(ptr->left,data);
        else                   //如果大于,放在右子树
            ptr->right = insertData(ptr->right,data);
        return ptr;
    }
}

int findData(node *ptr,int data)   //返回1表示存在,返回0表示不存在
{
    if(ptr == NULL)    //如果空,表示没找到,返回0
        return 0;
    else if(ptr->data == data)  //找到,返回1
        return 1;
    else if(data < ptr->data)   //如果小于当前节点值,则从左子树继续找
        return findData(ptr->left,data);
    else    //如果大于,则从右子树继续找
        return findData(ptr->right,data);
}

node *deleteData(node *ptr,int data) //在ptr指向节点的子树及自己中删除data
{
    if(ptr == NULL)   //如果空节点,则返回NULL
        return NULL;
    else if(data < ptr->data)  //如果小于,则从左子树继续进行
        ptr->left = deleteData(ptr->left,data); 
    else if(data > ptr->data) //如果大于,则从右子树继续进行
        ptr->right = deleteData(ptr->right,data);
    else if(ptr->left == NULL){ //如果没有左子树,则把右子树提上去
       node *temp = ptr->right;
       free(ptr);
       return temp;
    }
    else if(ptr->left->right == NULL){ //如果左儿子没有右儿子,把左儿子提上去
       node *temp = ptr->left;
       temp->right = ptr->right;
       free(ptr);
       return temp; 
    }
    else{                  //否则把左儿子最大子孙提上去
        node *temp;
        for(temp = ptr->left;temp->right->right != NULL;temp = temp->right)
            ;// 找到最大子孙的父亲
        node *temp2 = temp->right;  //temp2为最大子孙
        temp->right = temp2->left;  //最大子孙的父亲接过右儿子的左儿子
        temp2->left = ptr->left;  //最大子孙成功提上去,取代ptr
        temp2->right = ptr->right;
        free(ptr) ;
        return temp2;
    }
    return ptr;
}

int main(void)
{
    node *root;
    root = (node *)malloc(sizeof(node));
    root = NULL;
    root = insertData(root,8);
    root = insertData(root,3);
    root = insertData(root,10);
    root = insertData(root,1);
    root = insertData(root,6);
    root = insertData(root,14);
    root = insertData(root,4);
    root = insertData(root,7);
    root = insertData(root,13); //插入数据 
    print(root);
    cout<<"\n";

    cout<<findData(root,20)<<"\n";
    cout<<findData(root,10)<<"\n";   //查找数据 

    deleteData(root,10);   //删除数据 
    print(root);
    cout<<"\n";

    return 0;
}

ouput:
8 3 1 6 4 7 10 14 13
0
1
8 3 1 6 4 7 14 13


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值