C语言之二叉查找树

对于树的基本认识,我们很容易通过我们平常所见到的树来理解:一棵树,有一个根,根往上又会分叉出大树枝,大树枝又会分叉出小树枝,以此往复,直到最后是叶子。而作为数据结构的树也是类似的,只不过我们通常将它倒着画。树的应用也相当广泛,例如在文件系统,数据库索引中的应用等。本文会对树的基本概念做介绍,但重点介绍二叉查找树。

软件是通过数据和算法实现对现实世界的抽象,具有层次关系的数据在现实世界中能找到很多实例,比如:

  • 公司组织架构:董事长-CXO-总监-经理-主管-员工;
  • 中国行政区域划分:中国-省-市(县)-街道(小区)-门牌号;

在这里插入图片描述

二叉查找树,也称作二叉搜索树,有序二叉树,排序二叉树,而当一棵空树或者具有下列性质的二叉树,就可以被定义为二叉查找树:

  • 若任意节点的左子树不空,则左子树上所有节点的值均小于它的根节点的值;
  • 若任意节点的右子树不空,则右子树上所有节点的值均大于它的根节点的值;
  • 任意节点的左、右子树也分别为二叉查找树;
  • 没有键值相等的节点。

二叉查找树相比于其他数据结构的优势在查找、插入的时间复杂度较低,为O(log n)。二叉查找树是基础性数据结构,用于构建更为抽象的数据结构,如集合、multiset、关联数组等。对于大量的输入数据,链表的线性访问时间太慢,不宜使用。

查找

步骤:

  • 若根结点的关键字值等于查找的关键字,成功;
  • 若小于根结点的关键字值,递归查左子树;
  • 若大于根结点的关键字值,递归查右子树;
  • 若子树为空,查找不成功。

插入

首先执行查找算法,找出被插结点的父亲结点。 判断被插结点是其父亲结点的左、右儿子。将被插结点作为叶子结点插入。
若二叉树为空。则首先单独生成根结点。
注意:新插入的结点总是叶子结点。

删除

在二叉排序树删去一个结点,情况相对比较复杂,分三种情况讨论:

  • 若*p结点为叶子结点,即PL(左子树)和PR(右子树)均为空树。由于删去叶子结点不破坏整棵树的结构,则可以直接删除此子结点。
  • p结点只有左子树PL或右子树PR,此时只要令PL或PR直接成为其双亲结点f的左子树(当p是左子树)或右子树(当p是右子树)即可,作此修改也不破坏二叉排序树的特性。
  • p结点的左子树和右子树均不空。在删去p之后,为保持其它元素之间的相对位置不变,可按中序遍历保持有序进行调整,可以有两种做法: 其一是令p的左子树为f的左/右(依p是f的左子树还是右子树而定)子树,s为p左子树的最右下的结点,而p的右子树为s的右子树;
    其二是令p的直接前驱(或直接后继)替代p,然后再从二叉排序树中删去它的直接前驱(或直接后继)-即让f的左子树(如果有的话)成为p左子树的最左下结点(如果有的话),再让f成为p的左右结点的父结点。

遍历

常见的遍历主要分以下三种:

  • 前序遍历,先检查节点值(根节点),然后递归遍历左子树和右子树;
  • 中序遍历,先遍历左子树,然后检查当前节点值,最后遍历右子树;
  • 后序遍历,先递归遍历左右子树,然后检查当前节点值
/**********2020.8.27*******/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0

struct TreeNode;
typedef struct TreeNode *Position;
typedef struct TreeNode *SearchTree;
typedef int ElementType;

SearchTree MakeEmpty( SearchTree T );
Position Find( ElementType X, SearchTree T );
Position FindMin( SearchTree T );
Position FindMax( SearchTree T );
SearchTree Insert( ElementType X, SearchTree T );
SearchTree Delete( ElementType X, SearchTree T );
ElementType Retrieve( Position P );

typedef int Status;

struct TreeNode
{
    ElementType Element;//节点值
    SearchTree Left;    //左节点
    SearchTree Right;   //右节点
};

SearchTree MakeEmpty(SearchTree T)
{
    if (T != NULL)
    {
        MakeEmpty(T->Left);
        MakeEmpty(T->Right);
        free(T);
    }
    return NULL;
}
/*查找值为X的节点*/
Position Find(ElementType X, SearchTree T)
{
    if( T == NULL )    /*最后一层还没有找到*/
        return NULL;
    if (X < T->Element ) /*从左子树查找*/
        return Find(X, T->Left);
    else
    if (X > T->Element)  /*从右边子树查找*/
        return Find(X, T->Right);
    else
        return T; /*找到*/
}
/*找到一棵树中最小的节点*/
Position FindMin(SearchTree T)
{
    if ( T == NULL )
        return NULL;
    else
    if ( T-> Left == NULL )
        return T;
    else
        return FindMin( T->Left );
}
/*找到一棵树中最大的节点*/
Position FindMax(SearchTree T)
{
    if ( T != NULL )
        while(T->Right != NULL)
            T = T->Right;
    return T;
}
/*将X插入到树中*/
SearchTree Insert(ElementType X, SearchTree T)
{
    if (T == NULL)
    {

        /* Create and return a one-node tree */
        T =(struct TreeNode*) malloc(sizeof( struct TreeNode ));
        if ( T == NULL )
            printf("Out of space!!!\n");
        else
        {
            T->Element = X;  /*将节点信息存储在子节点中*/
            T->Left = T->Right = NULL;
        }
    }
    else if (X < T->Element)   /*比当前节点小,则插入到左子树*/
        T->Left = Insert(X, T->Left);
    else if (X > T->Element)   /*比当前节点大,则插入到右子树*/
        T->Right = Insert(X, T->Right);
    /* Else X is in the tree already; we'll do nothing */

    return T;
}
/*删除值为X的节点*/
SearchTree Delete(ElementType X, SearchTree T)
{
    Position TmpCell;
    if (T == NULL)
        printf("Element not found\n");
    /*比当前节点值小,从左子树查找并删除*/
    else if (X < T->Element) /* Go left */  
        T->Right = Delete(X, T->Left);
    /*比当前节点值大,从右子树查找并删除*/
    else if (X > T->Element) /* Go Right */  
        T->Right = Delete(X, T->Left);
    /*等于当前节点值,并且当前节点有左右子树*/
    else if (T->Left && T->Right) /* Two Children */
    {
        /*用右子树的最小值代替该节点,并且递归删除该最小值节点*/
        /* Replace with smallest in right subtree */
        TmpCell = FindMin(T->Right);
        T->Element = TmpCell->Element;
        T->Right = Delete(T->Element, T->Right);
    }
    /*要删除的节点只有一个子节点或没有子节点*/
    else /* One or zero children */
    {
        TmpCell = T;
        /*要删除节点有右孩子*/
        if (T->Left == NULL) /* Also handles 0 children */
            T = T->Right;
        /*要删除节点有左孩子*/
        else if (T->Right == NULL)
            T = T->Left;
        free( TmpCell );
    }

    return T;
}

ElementType Retrieve(Position P)
{
    return P->Element;
}

/**
 * 前序遍历"二叉树"
 * @param T Tree
 */
void PreorderTravel(SearchTree T)
{
    if (T != NULL)
    {
        printf("%d\n", T->Element);
        PreorderTravel(T->Left);
        PreorderTravel(T->Right);
    }
}

/**
 * 中序遍历"二叉树"
 * @param T Tree
 */
void InorderTravel(SearchTree T)
{
    if (T != NULL)
    {
        InorderTravel(T->Left);
        printf("%d\n", T->Element);
        InorderTravel(T->Right);
    }
}

/**
 * 后序遍历二叉树
 * @param T Tree
 */
void PostorderTravel(SearchTree T)
{
    if (T != NULL)
    {
        PostorderTravel(T->Left);
        PostorderTravel(T->Right);
        printf("%d\n", T->Element);
    }
}
/*打印树*/
void PrintTree(SearchTree T, ElementType Element, int direction)
{
    if (T != NULL)
    {
        if (direction == 0)
            printf("%2d is root\n", T->Element);
        else
            printf("%2d is %2d's %6s child\n", T->Element, Element, direction == 1 ? "right" : "left");

        PrintTree(T->Left, T->Element, -1);
        PrintTree(T->Right, T->Element, 1);
    }
}

int main(int argc, char const *argv[])
{
    printf("Hello Leon\n");
    SearchTree T;
    MakeEmpty(T);

    T = Insert(21, T);
    T = Insert(2150, T);
    T = Insert(127, T);
    T = Insert(121, T);

    printf("树的详细信息: \n");
    PrintTree(T, T->Element, 0);

    printf("前序遍历二叉树: \n");
    PreorderTravel(T);

    printf("中序遍历二叉树: \n");
    InorderTravel(T);

    printf("后序遍历二叉树: \n");
    PostorderTravel(T);
    
    printf("最大值: %d\n", FindMax(T)->Element);
    printf("最小值: %d\n", FindMin(T)->Element);
    return 0;
}

参考:
https://www.jianshu.com/p/8cb54efab7da
https://www.jianshu.com/p/802ac3148540
https://www.yanbinghu.com/2019/04/07/55964.html

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
下面是使用C语言实现二叉查找树的构造与遍历的示例代码: ```c #include <stdio.h> #include <stdlib.h> struct TreeNode { int val; struct TreeNode *left; struct TreeNode *right; }; // 向二叉查找树插入一个节点 struct TreeNode* insert(struct TreeNode* root, int val) { if (root == NULL) { struct TreeNode* node = (struct TreeNode*)malloc(sizeof(struct TreeNode)); node->val = val; node->left = NULL; node->right = NULL; return node; } if (val < root->val) { root->left = insert(root->left, val); } else if (val > root->val) { root->right = insert(root->right, val); } return root; } // 前序遍历二叉查找树 void preorderTraversal(struct TreeNode* root) { if (root == NULL) { return; } printf("%d ", root->val); preorderTraversal(root->left); preorderTraversal(root->right); } // 序遍历二叉查找树 void inorderTraversal(struct TreeNode* root) { if (root == NULL) { return; } inorderTraversal(root->left); printf("%d ", root->val); inorderTraversal(root->right); } // 后序遍历二叉查找树 void postorderTraversal(struct TreeNode* root) { if (root == NULL) { return; } postorderTraversal(root->left); postorderTraversal(root->right); printf("%d ", root->val); } int main() { // 构造二叉查找树 struct TreeNode* root = NULL; root = insert(root, 5); root = insert(root, 3); root = insert(root, 7); root = insert(root, 2); root = insert(root, 4); root = insert(root, 6); root = insert(root, 8); // 遍历二叉查找树 printf("Preorder Traversal: "); preorderTraversal(root); printf("\n"); printf("Inorder Traversal: "); inorderTraversal(root); printf("\n"); printf("Postorder Traversal: "); postorderTraversal(root); printf("\n"); return 0; } ``` 上述代码,我们使用了递归的方式实现了二叉查找树的构造和遍历。在构造二叉查找树时,我们使用了递归的方式将节点插入到树。在遍历二叉查找树时,我们分别实现了前序遍历、序遍历和后序遍历,并使用递归的方式完成遍历过程。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值