二叉搜索树(Binary Search Tree)--C语言描述

一:硬着头皮就是上

数据结构中有个东西一直不愿意去面对,就是二叉搜索树,以及平衡二叉树。想想就耗脑细胞

马上开学了,就要学C++了,还有其他的事,估计更没有时间搞数据结构了,于是狠下心,把二叉搜索树和平衡二叉树给拿下!!

啊啊啊啊,算法很枯燥无聊,不过搞明白了收获多多,不过目前好像没有什么用。

反正安慰自己,这都是内功,修炼好了,以后放大招威力无比啊,嘿嘿。

二:图解二叉搜索树概念

二叉树呢,其实就是链表的一个二维形式,而二叉搜索树,就是一种特殊的二叉树,这种二叉树有个特点:对任意节点而言,左孩子(当然了,存在的话)的值总是小于本身,而右孩子(存在的话)的值总是大于本身。

下面来介绍在此种二叉树结构上的查找,插入,删除算法思路。

查找:因为这种结构就是为了来方便查找的,所以查找其中的某个值很容易,从根开始,小的往左找,大的往右找,不大不小的就是这个节点了;

代码很简单,这里就不写了。

插入:插入一样的道理,从根开始,小的往左,大的往右,直到叶子,就插入。

代码:

int Insert(BSTree *T,data_type data)//插入数据
{
    BSTree newnode,p;
    newnode = (BSTree)malloc(sizeof(BSNode));
    newnode->lchild = newnode->rchild = NULL;
    newnode->data = data;
    if(*T == NULL)
    {
        *T = newnode;
    }
    else
    {
        p = *T;
        while(1)
        {
            if(data == p->data)
            {
                return 0;//数值已存在,不能插入
            }
            else if(data > p->data)
            {
                if(p->rchild == NULL)//右孩子为空,插入
                {
                    p->rchild = newnode;
                    return 1;
                }
                else        //继续向下找
                {
                    p = p->rchild;
                }
            }
            else
            {
                if(p->lchild == NULL)//左孩子为空,插入
                {
                    p->lchild = newnode;
                    return 1;
                }
                else        //继续向下找
                {
                    p = p->lchild;
                }
            }
        }
    }
}

删除:而结点的删除则比较麻烦,是这个结构中最难的一环,因为我们删除的结点不一定是叶子结点,是叶子结点很好办,但是如果是二叉树中的一个结点,则涉及到删除后的连接问题。

删除一共分为以下四种情况:

1.删除结点为叶子结点:这个就不说了,删除很简单。

2.删除结点不是叶子结点,其左子树为空,右子树不为空:这种情况,只需将其父结点与其右孩子结点连接,然后删除,例如删除下图的40,就是连接30与35。

3.删除结点不是叶子结点,其右子树为空,左子树不为空:这种情况,只需将其父结点与其左孩子结点连接,然后删除,例如删除下图的80,就是连接50和90

4.删除结点不是叶子结点,左右子树都为空,这种情况最复杂,咋们单独分析分析。

此种情况下,总的删除思想就是:把这个结点往下挪,直到它变为叶子为止。挪移规则:即和此结点下的最大孩子的值交换,把删除该结点转换为删除孩子结点的操作。

当然会出现这种情况,交换后此结点仍然不是叶子结点,那就继续按照挪移规则直到将改结点挪移成叶子结点。举例如图:

首先是我自己写的一个删除算法,借用了查找函数。

我的想法还是以前链表的删除思路,删除一个结点需要记录前一个结点,所以我就是用fa记录删除结点的父结点,然后操作的。

所以借用的查找函数就不仅仅只是返回结点地址,也要返回父结点地址。

BSTree Find(BSTree T,data_type data,BSTree *target) //查找函数,并返回父亲结点地址
{
    if(T == NULL)//二叉树为空
    {
        *target = NULL;
        return NULL;
    }
    else if(T->data == data)//树根即为要查找的结点
    {
        *target = T;
        return NULL;
    }
    else
    {
        while(T)
        {
            if(T->lchild && T->lchild->data == data)
            {
                *target = T->lchild;
                return T;
            }
            if(T->rchild && T->rchild->data == data)
            {
                *target = T->rchild;
                return T;
            }
            if(data > T->data)
            {
                T = T->rchild;
            }
            if(data < T->data)
            {
                T = T->lchild;
            }
        }
        return NULL;
    }
}

int Delete1(BSTree *T,data_type data)//删除
{
    BSTree fa,target;
    if(*T == NULL)//树为空
    {
        return 0;
    }
    fa = Find(*T,data,&target);//fa为删除结点的父结点,target为删除结点
    if(target->lchild == NULL)    //删除结点左子树为空,右子树不为空的情况(左右子树都为空的情况也适用)
    {
        if(fa == NULL && target->rchild == NULL)//树只存在根结点的情况
        {
            free(*T);
            *T = NULL;
            return 1;
        }
        if(fa->rchild == target)
        {
            fa->rchild = target->rchild;
            free(target);
            return 1;
        }
        if(fa->lchild == target)
        {
            fa->lchild = target->rchild;
            free(target);
            return 1;
        }
    }
    else if(target->rchild == NULL)//删除结点右子树为空,左子树不为空的情况
    {
        if(fa->rchild == target)
        {
            fa->rchild = target->lchild;
            free(target);
            return 1;
        }
        if(fa->lchild == target)
        {
            fa->lchild = target->lchild;
            free(target);
            return 1;
        }
    }
    else    //左右子树都不为空的情况
    {
        if(target->lchild->rchild == NULL)    //孩子结点中最大值结点为target->lchild
        {
            target->data = target->lchild->data;
            return Delete1(&(target->lchild),target->data);
        }
        else        //孩子结点中最大值结点为target->lchild->rchild.....->rchild
        {
            BSTree p1,p2;
            p2 = target->lchild;
            p1 = p2->rchild;
            while(p1->rchild != NULL)//循环找到最大值结点
            {
                p2 = p1;
                p1 = p1->rchild;
            }
            target->data = p1->data;
            return Delete1(&(p2->rchild),p1->data);
        }
    }
}

然后写完之后感觉自己的算法篇幅好大,貌似有点麻烦,于是在网上找了一下相关的二叉搜索树的删除算法,发现一个很精妙小巧的算法,将递归用的非常漂亮。

当然,算法思路都是差不多的,所以效率上应该没有什么很大的差别,不过这个算法递归思想用的很赞,值得学习一下。

建议先把上面我写的算法看明白了,在看下面的这个。

//a:删除叶子结点,只要将其双亲结点链接到它的指针置空即可。
//b:删除单支结点,只要将其后继指针链接到它所在的链接位置即可
//c:删除双支结点,一般采用的方法是首先把它的中序前驱结点的值赋给该结点的值域,
//然后再删除它的中序前驱结点,若它的中序前驱结点还是双支结点,继续对其做同样的操作,
//若是叶子结点或单支结点则做对应的操作,若是根结点则结束。

int Delete(BSTree *T,data_type data)
{
    BSTree temp;
    if(data < (*T)->data)//继续在左子树中查找
    {
        return Delete(&(*T)->lchild,data);
    }
    if(data > (*T)->data)//继续在右子树中查找
    {
        return Delete(&(*T)->rchild,data);
    }
    //代码运行到此处,则找到data,即为(*T)->data
    temp = *T;//*T:删除结点地址,T:记录删除结点地址的指针,即为删除结点的父结点的左/右指针
    if((*T)->lchild == NULL)//删除结点的左子树为空,将整个右子树作为树根(此处也包含叶子结点情况)
    {
        (*T) = (*T)->rchild;
        free(temp);
        return 1;
    }
    else if((*T)->rchild == NULL)//删除结点的右子树为空,将整个左子树作为树根
    {
        (*T) = (*T)->lchild;
        free(temp);
        return 1;
    }
    else//删除结点的左右子树都不为空,找左子树的的最大值结点
    {
        if((*T)->lchild->rchild == NULL)//最大值结点为左孩子的情况
        {
            (*T)->data = (*T)->lchild->data;
            Delete(&((*T)->lchild),(*T)->data);
        }
        else        //最大值结点为最深的右孩子结点情况
        {
            BSTree p1,p2;
            p2 = (*T)->lchild;
            p1 = p2->rchild;
            while(p1->rchild != NULL)
            {
                p2 = p1;
                p1 = p1->rchild;
            }
            (*T)->data = p1->data;
            Delete(&(p2->rchild),p1->data);
        }
    }
}

二:二叉搜索树完整代码(C语言)

#include <stdio.h>
#include <stdlib.h>
#include <conio.h>

typedef int data_type;

typedef struct bst
{
    data_type data;
    struct bst *lchild,*rchild;
}BSNode,*BSTree;

BSTree Find(BSTree T,data_type data,BSTree *target) //查找函数,并返回父亲结点地址
{
    if(T == NULL)//二叉树为空
    {
        *target = NULL;
        return NULL;
    }
    else if(T->data == data)//树根即为要查找的结点
    {
        *target = T;
        return NULL;
    }
    else
    {
        while(T)
        {
            if(T->lchild && T->lchild->data == data)
            {
                *target = T->lchild;
                return T;
            }
            if(T->rchild && T->rchild->data == data)
            {
                *target = T->rchild;
                return T;
            }
            if(data > T->data)
            {
                T = T->rchild;
            }
            if(data < T->data)
            {
                T = T->lchild;
            }
        }
        return NULL;
    }
}

int Insert(BSTree *T,data_type data)//插入数据
{
    BSTree newnode,p;
    newnode = (BSTree)malloc(sizeof(BSNode));
    newnode->lchild = newnode->rchild = NULL;
    newnode->data = data;
    if(*T == NULL)
    {
        *T = newnode;
    }
    else
    {
        p = *T;
        while(1)
        {
            if(data == p->data)
            {
                return 0;//数值已存在,不能插入
            }
            else if(data > p->data)
            {
                if(p->rchild == NULL)//右孩子为空,插入
                {
                    p->rchild = newnode;
                    return 1;
                }
                else        //继续向下找
                {
                    p = p->rchild;
                }
            }
            else
            {
                if(p->lchild == NULL)//左孩子为空,插入
                {
                    p->lchild = newnode;
                    return 1;
                }
                else        //继续向下找
                {
                    p = p->lchild;
                }
            }
        }
    }
}

int Delete1(BSTree *T,data_type data)//删除
{
    BSTree fa,target;
    if(*T == NULL)//树为空
    {
        return 0;
    }
    fa = Find(*T,data,&target);//fa为删除结点的父结点,target为删除结点
    if(target->lchild == NULL)    //删除结点左子树为空,右子树不为空的情况(左右子树都为空的情况也适用)
    {
        if(fa == NULL && target->rchild == NULL)//树只存在根结点的情况
        {
            free(*T);
            *T = NULL;
            return 1;
        }
        if(fa->rchild == target)
        {
            fa->rchild = target->rchild;
            free(target);
            return 1;
        }
        if(fa->lchild == target)
        {
            fa->lchild = target->rchild;
            free(target);
            return 1;
        }
    }
    else if(target->rchild == NULL)//删除结点右子树为空,左子树不为空的情况
    {
        if(fa->rchild == target)
        {
            fa->rchild = target->lchild;
            free(target);
            return 1;
        }
        if(fa->lchild == target)
        {
            fa->lchild = target->lchild;
            free(target);
            return 1;
        }
    }
    else    //左右子树都不为空的情况
    {
        if(target->lchild->rchild == NULL)    //孩子结点中最大值结点为target->lchild
        {
            target->data = target->lchild->data;
            return Delete1(&(target->lchild),target->data);
        }
        else        //孩子结点中最大值结点为target->lchild->rchild.....->rchild
        {
            BSTree p1,p2;
            p2 = target->lchild;
            p1 = p2->rchild;
            while(p1->rchild != NULL)//循环找到最大值结点
            {
                p2 = p1;
                p1 = p1->rchild;
            }
            target->data = p1->data;
            return Delete1(&(p2->rchild),p1->data);
        }
    }
}

//a:删除叶子结点,只要将其双亲结点链接到它的指针置空即可。
//b:删除单支结点,只要将其后继指针链接到它所在的链接位置即可
//c:删除双支结点,一般采用的方法是首先把它的中序前驱结点的值赋给该结点的值域,
//然后再删除它的中序前驱结点,若它的中序前驱结点还是双支结点,继续对其做同样的操作,
//若是叶子结点或单支结点则做对应的操作,若是根结点则结束。

int Delete(BSTree *T,data_type data)
{
    BSTree temp;
    if(data < (*T)->data)//继续在左子树中查找
    {
        return Delete(&(*T)->lchild,data);
    }
    if(data > (*T)->data)//继续在右子树中查找
    {
        return Delete(&(*T)->rchild,data);
    }
    //代码运行到此处,则找到data,即为(*T)->data
    temp = *T;//*T:删除结点地址,T:记录删除结点地址的指针,即为删除结点的父结点的左/右指针
    if((*T)->lchild == NULL)//删除结点的左子树为空,将整个右子树作为树根(此处也包含叶子结点情况)
    {
        (*T) = (*T)->rchild;
        free(temp);
        return 1;
    }
    else if((*T)->rchild == NULL)//删除结点的右子树为空,将整个左子树作为树根
    {
        (*T) = (*T)->lchild;
        free(temp);
        return 1;
    }
    else//删除结点的左右子树都不为空,找左子树的的最大值结点
    {
        if((*T)->lchild->rchild == NULL)//最大值结点为左孩子的情况
        {
            (*T)->data = (*T)->lchild->data;
            Delete(&((*T)->lchild),(*T)->data);
        }
        else        //最大值结点为最深的右孩子结点情况
        {
            BSTree p1,p2;
            p2 = (*T)->lchild;
            p1 = p2->rchild;
            while(p1->rchild != NULL)
            {
                p2 = p1;
                p1 = p1->rchild;
            }
            (*T)->data = p1->data;
            Delete(&(p2->rchild),p1->data);
        }
    }
}

void CreateBSTree(BSTree *T)//创建二叉树,调用插入算法创建
{
    data_type data;
    char ch;
    printf("请输入要创建的二叉搜索树的数据(数据之间用空格隔开,输入完毕按回车):");
    do
    {
        scanf("%d",&data);
        ch = getchar();
        Insert(T,data);
    }
    while (ch != 10);
}

void Inorder(BSTree T)
{
    if(T)
    {
        Inorder(T->lchild);
        printf("%d ",T->data);
        Inorder(T->rchild);
    }
}

int main()
{
    BSTree T,fa,target;
    T = NULL;
    CreateBSTree(&T);
    Inorder(T);
    int select;
    data_type data;
    while(1)
    {
        printf("1.插入  2.删除 3.中序遍历\n");
        scanf("%d",&select);
        if(select == 1)
        {
            printf("请输入要插入的数据:");
            scanf("%d",&data);
            Insert(&T,data);
            Inorder(T);
            getch();
        }
        else if(select == 2)
        {
            printf("删除数据:");
            scanf("%d",&data);
            Delete1(&T,data);
            Inorder(T);
            getch();
        }
        else if(select == 3)
        {
            Inorder(T);
            getch();
        }
    }
    return 0;
}
/* 50 30 80 20 40 90 35 85 32 88*/
时间: 2024-08-01 13:51:16

二叉搜索树(Binary Search Tree)--C语言描述的相关文章

编程算法 - 二叉搜索树(binary search tree) 代码(C)

二叉搜索树(binary search tree) 代码(C) 本文地址: http://blog.csdn.net/caroline_wendy 二叉搜索树(binary search tree)能够高效的进行插入, 查询, 删除某个元素, 时间复杂度O(logn). 简单的实现方法例如以下. 代码: /* * main.cpp * * Created on: 2014.7.20 * Author: spike */ /*eclipse cdt, gcc 4.8.1*/ #include <s

二叉搜索树(Binary Search Tree)

1.什么是二叉搜索树 二叉搜索树(Binary Search Tree)是一棵有序的二叉树,所以我们也可以称它为二叉排序树(不知道二叉树的童鞋,先看看二叉树:传送门).具有以下性质的二叉树我们称之为二叉搜索树:若它的左子树不为空,那么左子树上的所有值均小于它的根节点:若它的右子树不为空,那么右子树上所有值均大于它的根节点.它的左子树和右子树分别也为二叉搜索树. 2.二叉搜索树的结构 二叉搜索树能够高效的进行一下操作:①插入一个数值②查询是否包含某个数值③删除某个数值 根据实现的不同,还可以实现其

编程算法 - 二叉搜索树(binary search tree) 集合(set)和映射(map) 代码(C)

二叉搜索树(binary search tree) 集合(set)和映射(map) 代码(C++) 本文地址: http://blog.csdn.net/caroline_wendy 二叉搜索树(binary search tree)作为常用而高效的数据结构, 标准库中包含实现, 在标准库的集合(set)和映射(map), 均使用. 具体操作代码如下. 代码: /* * main.cpp * * Created on: 2014.7.20 * Author: spike */ /*eclipse

二叉排序树BinarySortTree(二叉搜索树Binary Search Tree)

二叉排序树(Binary Sort Tree)又称二叉查找树(Binary Search Tree),亦称二叉搜索树. 定义: 二叉排序树或者是一棵空树,或者是具有下列性质的二叉树: (1)若左子树不空,则左子树上所有结点的值均小于它的根结点的值: (2)若右子树不空,则右子树上所有结点的值均大于或等于它的根结点的值: (3)左.右子树也分别为二叉排序树: (4)没有键值相等的节点 步骤: 二叉树 若根结点的关键字值等于查找的关键字则成功. 否则,若小于根结点的关键字值,递归查左子树. 若大于根

数据结构-二叉搜索树(Binary Search Tree)的C++实现模板

笔者最近开始学习了二叉树这种数据结构,于是写出了一个二叉树的实现~ 二叉树真是个好东西 =.= 该图显示了在二叉树中插入一个节点的步骤...下面就用这个二叉树做测试好了 /** "BST.h"  * The Binary Search Tree Data Structure in C++  * Time Cost : Inorder / Preorder / Postorder Traversal : O(n)  *             Search / Find / Insert

PTA Build A Binary Search Tree(C语言)

题目 A Binary Search Tree (BST) is recursively defined as a binary tree which has the following properties: The left subtree of a node contains only nodes with keys less than the node's key. The right subtree of a node contains only nodes with keys gre

LeetCode 235. 二叉搜索树的最近公共祖先(Lowest Common Ancestor of a Binary Search Tree) 32

235. 二叉搜索树的最近公共祖先 235. Lowest Common Ancestor of a Binary Search Tree 题目描述 给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先. 百度百科中最近公共祖先的定义为:"对于有根树 T 的两个结点 p.q,最近公共祖先表示为一个结点 x,满足 x 是 p.q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)." 例如,给定如下二叉搜索树: root = [6,2,8,0,4,7,9,null,nul

树, 二叉树, 二叉搜索树

转载:Vamei   出处:http://www.cnblogs.com/vamei 树的特征和定义 树(Tree)是元素的集合.我们先以比较直观的方式介绍树.下面的数据结构是一个树: 树有多个节点(node),用以储存元素.某些节点之间存在一定的关系,用连线表示,连线称为边(edge).边的上端节点称为父节点,下端称为子节点.树像是一个不断分叉的树根. 每个节点可以有多个子节点(children),而该节点是相应子节点的父节点(parent).比如说,3,5是6的子节点,6是3,5的父节点:1

纸上谈兵: 树, 二叉树, 二叉搜索树

树的特征和定义 树(Tree)是元素的集合.我们先以比较直观的方式介绍树.下面的数据结构是一个树: 树有多个节点(node),用以储存元素.某些节点之间存在一定的关系,用连线表示,连线称为边(edge).边的上端节点称为父节点,下端称为子节点.树像是一个不断分叉的树根. 每个节点可以有多个子节点(children),而该节点是相应子节点的父节点(parent).比如说,3,5是6的子节点,6是3,5的父节点:1,8,7是3的子节点, 3是1,8,7的父节点.树有一个没有父节点的节点,称为根节点(

二叉树、二叉搜索树、AVL树的java实现

数据结构一直都是断断续续的看,总是觉得理解的不够深入,特别是对树的理解,一直都很浅显,今儿又看了一遍,来做个总结吧. 首先,树中的一些概念: 1.树的节点包含一个数据元素,以及若干指向其子树的分支.节点拥有的子树的数量称为节点的度.节点的最大层次称为树的深度或高度. 2.二叉树是一种树形结构,其特点是每个节点至多有两棵子树,且子树有左右之分,次序不能随意颠倒. 3.满二叉树:一棵深度为k且有2^k - 1个节点的二叉树,称之为满二叉树. 4.完全二叉树:对一个深度为k,节点个数为n的二叉树,当且