C语言实现二叉查找树(BST)的基本操作

我们在上一篇博客中讲解了二叉树,这一次我们来实现二叉树的进阶——二叉查找树(Binary Search Tree),又称二插排序树(Binary Sort Tree)。所以简称为BST。二插查找树的定义如下:

1.若左子树不为空,则左子树上所有节点的值均小于它的根节点的值;

2.若右子树不为空,则右子树上所有节点的值均大于它的根节点的值;

3.左右子树也分别为二叉排序树;

二叉排序树的一个重要特点就是中序遍历是一个递增序列。示例代码上传至: https://github.com/chenyufeng1991/BinarySearchTree  。

(1)节点的构造

typedef int elemType;
typedef struct BTNode{

    int data;
    struct BTNode *lChild;
    struct BTNode *rChild;
}BiTNode,*BiTree;

(2)创建二叉树

创建二插排序树的过程就是一个不断插入节点的过程,并且最重要的就是查找插入的合适位置。

//创建二叉查找树
/**
 *  输入-1时创建结束,其实是一个不断插入的过程
 */
int CreateBinarySearchTree(BiTree *T){

    printf("请输入创建二叉查找树的数字序列:\n");

    int num;
    scanf("%d",&num);

    while (num != -1) {
        Insert(T,num);
        scanf("%d",&num);
    }

    printf("%s函数执行,二叉查找树创建成功\n",__FUNCTION__);
    return 1;
}

(3)插入节点

//插入节点
void Insert(BiTree *T,int x){

    //这里创建一个要插入的节点
    BiTNode *pInsert = (BiTNode *)malloc(sizeof(BiTNode));
    pInsert->data = x;
    pInsert->lChild = NULL;
    pInsert->rChild = NULL;

    if ((*T) == NULL) {
        *T = pInsert;
    }

    if ((*T)->lChild == NULL && x < (*T)->data) {
        (*T)->lChild = pInsert;
    }

    if ((*T)->rChild == NULL && x > (*T)->data) {
        (*T)->rChild = pInsert;
    }

    //递归实现
    if (x < (*T)->data) {
        Insert(&(*T)->lChild, x);
    }

    if (x > (*T)->data) {
        Insert(&(*T)->rChild, x);
    }

    return;
}

(4)树的中序遍历和先序遍历

树的先序遍历+中序遍历可以唯一的确定一棵树。并且二叉排序树的中序遍历必定是一个递增的序列。用这样的方法可以来验证我们对一颗二叉排序树进行操作后是否正确。

//中序遍历二叉查找树
//打印的应该是一个递增的序列
void MiddleOrder(BiTree T){
    if (T == NULL) {
        return;
    }else{

        MiddleOrder(T->lChild);
        printf("%d ",T->data);
        MiddleOrder(T->rChild);
    }
}

//先序遍历二叉查找树
//因为先序遍历+中序遍历 可以唯一确定一棵树,等下可以验证树是否正确
void PreOrder(BiTree T){

    if (T == NULL) {
        return;
    }else{

        printf("%d ",T->data);
        PreOrder(T->lChild);
        PreOrder(T->rChild);
    }
}

(5)查找元素

//查找某一个值
//返回1表示找到该值,返回0表示没有找到
BiTNode *SearchValue(BiTree T,int x){
    if (T == NULL) {
        return 0;
    }else{
        if (x < T->data) {
            //查左子树
            SearchValue(T->lChild, x);
        }else if (x > T->data){
            //查右子树
            SearchValue(T->rChild, x);
        }else{
            //找到该值
            printf("该值的内存地址为:%p\n",T);
            return T;
        }
    }

    return NULL;
}

(6)删除元素

//删除某一个元素
BiTree *DeleteValue(BiTree *T,int x){

    //先要查找该节点,同时也要保存该节点的父节点
    BiTNode *searchNode;
    BiTNode *parentNode;

    //初始都指向根节点
    searchNode = *T;
    parentNode = *T;

    while (searchNode->data != x) {

        if (searchNode->lChild == NULL && searchNode->rChild == NULL) {
            //已经是叶子节点了,但是还没有找到该值,表示树种根本就没有要删除的节点
            printf("%s函数执行,不存在该值,删除失败\n",__FUNCTION__);
            return T;
        }

        if (x < searchNode->data) {

            parentNode = searchNode;
            searchNode = searchNode->lChild;
        }else if (x > searchNode->data){

            parentNode = searchNode;
            searchNode = searchNode->rChild;
        }else{
            break;
        }
    }

    if (searchNode->lChild == NULL && searchNode->rChild == NULL) {
        //是叶子节点
        if ((*T)->lChild == NULL && (*T)->rChild == NULL) {
            //是根节点
            free(*T);
            *T = NULL;
        }else{
            //不是根节点,是普通的叶子节点
            //需要判断要删除的节点是父节点的左孩子还是右孩子
            if (searchNode->data < parentNode->data) {
                //是左孩子
                parentNode->lChild = NULL;
            }else{
                //是右孩子
                parentNode->rChild = NULL;
            }
            free(searchNode);
            searchNode = NULL;
        }

        return T;
    }

    if (searchNode->lChild != NULL && searchNode->rChild == NULL) {
        //有左子树,没有右子树
        //直接把父节点的指针指向右子树即可,然后释放自己;
        //首先需要判断当前节点是父节点的左孩子还是右孩子
        if (searchNode->data < parentNode->data) {
            //是左孩子
            parentNode->lChild = searchNode->lChild;
        }else{
            //是右孩子
            parentNode->rChild = searchNode->lChild;
        }

        free(searchNode);
        searchNode = NULL;

        return T;
    }

    if (searchNode->lChild == NULL && searchNode->rChild != NULL) {
        //没有左子树,有右子树
        if (searchNode->data < parentNode->data) {
            //是左孩子
            parentNode->lChild = searchNode->rChild;
        }else{
            //是右孩子
            parentNode->rChild = searchNode->rChild;
        }

        free(searchNode);
        searchNode = NULL;

        return T;
    }

    if (searchNode->lChild != NULL && searchNode->rChild != NULL) {
        //要删除的节点既有左孩子,又有右孩子
        /**
         *  算法:删除节点p的左子树和右子树均不为空。找到p的后继y,因为y一定没有左子树,所以可以删除y,并让y的父节点成为y的右子树的父节点,并用y的值代替p的值;

         如何找到要删除节点的后继节点,包括该后继节点的父节点!!
         */

        BiTNode *nextNode;//寻找要删除节点的后继节点
        BiTNode *nextParentNode;//寻找要删除节点的后继节点的父节点
        nextParentNode = searchNode;
        nextNode = searchNode->rChild;
        while (nextNode->lChild != NULL) {
            nextParentNode = nextNode;
            nextNode = nextNode->lChild;
        }

        //这里要判断nextNode节点和nextParentNode节点的值大小,因为需要判断要删除节点是父亲的左孩子还是右孩子
        if (nextNode->data < nextParentNode->data) {
            //是左孩子
            nextParentNode->lChild = nextNode->rChild;
        }else{
            //是右孩子
            nextParentNode->rChild = nextNode->rChild;
        }

        //代替值
        searchNode->data = nextNode->data;

        //删除后继节点
        free(nextNode);
        nextNode = NULL;

        return T;
    }

    return T;
}

(7)测试代码

int main(int argc, const char * argv[]) {

    BiTree tree;

    //这个是引用传递
    CreateBinarySearchTree(&tree);

    MiddleOrder(tree);
    printf("\n");

    printf("请输入要查找的元素:");
    int searchValue;
    scanf("%d",&searchValue);
    SearchValue(tree,searchValue);

    printf("请输入要删除的元素:");
    int deleteValue;
    scanf("%d",&deleteValue);
    DeleteValue(&tree, deleteValue);
    printf("先序遍历:");
    PreOrder(tree);
    printf("\n中序遍历:");
    MiddleOrder(tree);//遍历检查
    printf("\n");

    return 0;
}
时间: 2024-10-22 17:52:22

C语言实现二叉查找树(BST)的基本操作的相关文章

二叉查找树BST 模板

二叉查找树BST 就是二叉搜索树 二叉排序树. 就是满足 左儿子<父节点<右儿子 的一颗树,插入和查询复杂度最好情况都是logN的,写起来很简单. 根据BST的性质可以很好的解决这些东西 1.查询值 int Search(int k,int x) { if(x<a[k].key && a[k].l) Search(a[k].l,x); else if(x>a[k].key && a[k].r) Search(a[k].r,x); else retur

二叉查找树(BST),平衡二叉查找树(AVL),红黑树(RBT),B~/B+树(B-tree)的比较

http://www.iteye.com/topic/614070 此少侠总结的特棒,直接收藏了. 我们这个专题介绍的动态查找树主要有: 二叉查找树(BST),平衡二叉查找树(AVL),红黑树(RBT),B~/B+树(B-tree).这四种树都具备下面几个优势: (1) 都是动态结构.在删除,插入操作的时候,都不需要彻底重建原始的索引树.最多就是执行一定量的旋转,变色操作来有限的改变树的形态.而这些操作所付出的代价都远远小于重建一棵树.这一优势在<查找结构专题(1):静态查找结构概论 >中讲到

详解二叉查找树(BST)

详解二叉查找树(BST) 本篇随笔简单讲解一下数据结构--二叉查找树(\(Binary\,\,Sort\,\,Tree,BST\)),(后文的"二叉查找树"一词均用\(BST\)代替). BST的概念 首先,\(BST\)是一棵二叉树. 它的定义是,根节点左子树全部严格小于根节点,右子树大于等于根节点,并且,左右子树都是\(BST\). 很好理解,很明朗很简单的定义. 可以看出,这是一个通过递归方式定义的数据结构,所以,它的诸多操作自然要用到递归. BST的功能 我们可以看出来,这个二

C语言实现二叉查找树

#include<stdio.h> #include<stdlib.h> /* * 数据结构:二叉查找树,即左孩子<父节点<右孩子 * C语言实现 * 2015-9-13 */ typedef struct TreeNode *PtrToNode; typedef PtrToNode Tree; typedef PtrToNode Position; struct TreeNode { int Element; Tree Left; //节点左孩子 Tree Right

【查找结构 2】二叉查找树 [BST]

当所有的静态查找结构添加和删除一个数据的时候,整个结构都需要重建.这对于常常需要在查找过程中动态改变数据而言,是灾难性的.因此人们就必须去寻找高效的动态查找结构,我们在这讨论一个非常常用的动态查找树——二叉查找树 . 二叉查找树的特点 下面的图就是两棵二叉查找树,我们可以总结一下他的特点: (1) 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值 (2) 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值(3) 它的左.右子树也分别为二叉查找树 我们中序遍历这两棵树发现一个

C语言实现循环队列(基本操作及图示)

-------------------------------------------- 如果使用顺序表作为队列的话,当处于右图状态则不能继续插入新的队尾元素,否则会因为数组越界而导致程序代码被破坏. 由此产生了由链表实现的循环队列,只有队列未满时才可以插入新的队尾元素. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

C语言实现链表队列(基本操作及图示)

-------------------------------------------- 基本概念: 和栈相反,队列是一种先进先出(FIFO)的线性表.只允许在一端插入,在另一端删除. 允许插入的叫"队尾"(rear),允许删除的叫"队头"(front). 使用场景:操作系统的作业排队.在允许多道程序运行的计算机系统中,同时有几个作业运行.如果运行结果都需要通道输出,则按照请求输出的先后次序排队.每当通道传输完毕可以接受新的输出任务时,队头的作业先从队列中退出作输出

二叉查找树(BST树)

二叉查找树的特点: 在二叉查找树中左子树上所有结点的数据都小于等于根结点的数据,而右子树上所有结点的数据都大于根结点的数据 1 //存储结构: 2 struct node 3 { 4 Int data; 5 node *lchild; 6 node *rchild; 7 }; 8 9 //在建树前根节点不存在: 10 Node *root = NULL; 11 12 //新建结点: 13 node *newNode(int v) 14 { 15 node *Node = new node; 16

C语言实现单链表的基本操作及其部分面试题

//单链表的基本数据类型及其结构 typedef int DataType; typedef struct LinkNode { DataType data; struct LinkNode *next; }LinkNode,*pLinkNode,*pLinkList; //单链表的初始化 void InitLinkList(pLinkList* pHead)//初始化 { assert(pHead); *pHead = NULL; } //当尾插,头插,插入时都需要建立一个新的结点,为方便建立