C语言实现二叉查找树

基本概念

首先二叉查找树是二叉树,回顾一下二叉树的概念,二叉树的每个节点都不能有多于两个的儿子,一般二叉树如下

《C语言实现二叉查找树》

二叉树的性质

二叉树有以下几个性质:TODO(上标和下标)
性质1:二叉树第i层上的结点数目最多为 2{i-1} (i≥1)。
性质2:深度为k的二叉树至多有2{k}-1个结点(k≥1)。
性质3:包含n个结点的二叉树的高度至少为log2 (n+1)。
性质4:在任意一棵二叉树中,若终端结点的个数为n0,度为2的结点数为n2,则n0=n2+1。

性质1:二叉树第i层上的结点数目最多为 2{i-1} (i≥1)

证明:下面用”数学归纳法”进行证明。
(01) 当i=1时,第i层的节点数目为2{i-1}=2{0}=1。因为第1层上只有一个根结点,所以命题成立。
(02) 假设当i>1,第i层的节点数目为2{i-1}。这个是根据(01)推断出来的!
下面根据这个假设,推断出”第(i+1)层的节点数目为2{i}”即可。
由于二叉树的每个结点至多有两个孩子,故”第(i+1)层上的结点数目” 最多是 “第i层的结点数目的2倍”。即,第(i+1)层上的结点数目最大值=2×2{i-1}=2{i}。
故假设成立,原命题得证!

性质2:深度为k的二叉树至多有2{k}-1个结点(k≥1)

证明:在具有相同深度的二叉树中,当每一层都含有最大结点数时,其树中结点数最多。利用”性质1”可知,深度为k的二叉树的结点数至多为:
20+21+…+2k-1=2k-1
故原命题得证!

性质3:包含n个结点的二叉树的高度至少为log2 (n+1)

证明:根据”性质2”可知,高度为h的二叉树最多有2{h}–1个结点。反之,对于包含n个节点的二叉树的高度至少为log2(n+1)。

性质4:在任意一棵二叉树中,若终端结点的个数为n0,度为2的结点数为n2,则n0=n2+1

证明:因为二叉树中所有结点的度数均不大于2,所以结点总数(记为n)=”0度结点数(n0)” + “1度结点数(n1)” + “2度结点数(n2)”。由此,得到等式一。
(等式一) n=n0+n1+n2
  另一方面,0度结点没有孩子,1度结点有一个孩子,2度结点有两个孩子,故二叉树中孩子结点总数是:n1+2n2。此外,只有根不是任何结点的孩子。故二叉树中的结点总数又可表示为等式二。
(等式二) n=n1+2n2+1
由(等式一)和(等式二)计算得到:n0=n2+1。原命题得证!

满二叉树,完全二叉树和二叉查找树

满二叉树

定义:高度为h,并且由2{h} –1个结点的二叉树,被称为满二叉树。

《C语言实现二叉查找树》

完全二叉树

定义:一棵二叉树中,只有最下面两层结点的度可以小于2,并且最下一层的叶结点集中在靠左的若干位置上。这样的二叉树称为完全二叉树。
特点:叶子结点只能出现在最下层和次下层,且最下层的叶子结点集中在树的左部。显然,一棵满二叉树必定是一棵完全二叉树,而完全二叉树未必是满二叉树。

《C语言实现二叉查找树》

二叉查找树

定义:二叉查找树(Binary Search Tree),又被称为二叉搜索树。设x为二叉查找树中的一个结点,x节点包含关键字key,节点x的key值记为key[x]。如果y是x的左子树中的一个结点,则key[y] <= key[x];如果y是x的右子树的一个结点,则key[y] >= key[x]。

《C语言实现二叉查找树》

在二叉查找树中:
(01) 若任意节点的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
(02) 任意节点的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
(03) 任意节点的左、右子树也分别为二叉查找树。
(04) 没有键值相等的节点(no duplicate nodes)。

在实际应用中,二叉查找树的使用比较多。下面,用C语言实现二叉查找树。

首先定义头文件

#ifndef BSTREE_H_INCLUDED
#define BSTREE_H_INCLUDED

typedef int elementType;//定义结点值类型
//定义结点
typedef struct node
{
    elementType key;
    int height;//当前结点高度
    struct node *left;
    struct node *right;
    struct node *parent;
} bsNode, *bsTree;

bsNode* create_node(elementType key,bsNode *left,bsNode *right,bsNode *parent);//创建结点

bsTree bstree_insert(bsTree tree,elementType key);

bsNode* insert_node(bsTree tree ,bsNode *node);//向树中插入结点

bsNode* dele_node(bsTree tree ,bsNode *node);//删除结点

void pre_order_tree(bsTree tree);

void in_order_tree(bsTree tree);

void post_order_tree(bsTree tree);

bsNode *maximum_node(bsTree tree);//寻找树中最大结点

bsNode *minimun_node(bsTree tree);//寻找树中最小结点

int get_tree_height(bsTree tree);

#endif // BSTREE_H_INCLUDED

在bstree.c中定义头文件中申明的函数

一棵二叉树是有由一个个结点组成的,我们先定义一个创建结点的函数

bsNode* create_node(elementType key,bsNode *left,bsNode *right,bsNode *parent)
{
    bsNode *node;
    node=(bsNode *)malloc(sizeof(bsNode));
    if(node==NULL)
    {
        printf("结点创建失败");
    }
    node->key=key;
    node->left=left;
    node->right=right;
    node->parent=parent;
    return node;
}

插入操作

有了结点后我们需要将一个个结点插入二叉树才行吧,其中插入操作用temp指针指向当前结点位置,因为二叉查找树是有序树,所以我们要先找到要插入结点该插入的位置,当temp从while中跳出来时候,说明已经找到要插入的位置了,pre指针指向temp前一个位置,也就是说while结束时pre是要插入结点node的父节点

bsNode* insert_node(bsTree tree ,bsNode *node)
{
    if(node==NULL)
    {
        return NULL;
    }
    bsNode *temp,*pre;
    pre=NULL;
    temp=tree;
    while(temp)
    {
        pre=temp;
        if(temp->key<node->key)
        {
            temp=temp->right;
        }
        else
        {
            temp=temp->left;
        }
    }
    //while结束循环时pre指向node的父节点
    if(tree==NULL)
    {
        tree=node;
    }
    else
    {
        node->parent=pre;
        if(pre->key<node->key)
        {
            pre->right=node;
        }
        else
        {
            pre->left=node;
        }
    }
    return tree;
}

bsTree bstree_insert(bsTree tree,elementType key)
{
    bsNode *node=create_node(key,NULL,NULL,NULL);
    return insert_node(tree,node);
}

删除操作

删除操作需要根据要删除结点node的位置分情况讨论

A.node没有同时有左右孩子结点,也就是说node是叶子或者node有独子,那么可以通过将node的父结点指向node的孩子就行(没有孩子就指向kong)
B.node有左右孩子,那么一般的策略是将node右子树中最小结点的值赋值给node,这样就保证了二叉树的有序性,同时将右子树中最小结点删除。
/* 删除操作需要讨论要删除结点node的孩子情况 1.node没有两个孩子,也就是node有独子或者无子,那么直接将node父节点指向node的孩子(node无子也就指向null),然后free(node) 2.node有两个孩子,一般策略是将node右孩子子树中最小的结点赋值给node,然后删除最小孩子结点。(这么做会导致二叉树越来越失衡,后面AVL会讲到可以先比较node左右孩子结点的深度, 取深度大的一边赋值给node,然后删除相应的结点,其中node左子树那么就取最大的结点,右子树就取最小的) */
bsNode* dele_node(bsTree tree ,bsNode *node)
{
    if(node==NULL||tree==NULL)
        return tree;
    if(tree->key>node->key)//要删除的结点在tree的左子树
    {
        tree->left= dele_node(tree->left,node);

    }
    else if(tree->key<node->key)//要删除的结点在右子树
    {
        tree->right= dele_node(tree->right,node);
    }
    else//找到要删除的结点了
    {
        //node结点有左右孩子
        if(tree->left&&tree->right)
        {
            bsNode *min=minimun_node(tree->right);
            tree->key=min->key;
            tree->right=dele_node(tree->right,min);
        }
        else
        {
            /* if(tree->left==NULL) { return tree->right;//右孩子是不是NULL无所谓 } else { return tree->left; } */
            return tree=tree->left?tree->left:tree->right;//node左子树不为空就返回,为空就返回右子树(不管右子树是不是null)
        }
    }
}

删除操作中涉及一个求最小结点的函数

bsNode *minimun_node(bsTree tree)
{
    if(tree->left)
    {
        tree=tree->left;
    }
    return tree;
}

最后就是遍历二叉树

//前序遍历二叉树
void pre_order_tree(bsTree tree)
{
    if(tree)
    {
        printf("%d ",tree->key);
        pre_order_tree(tree->left);
        pre_order_tree(tree->right);
    }
}
void in_order_tree(bsTree tree)
{
    if(tree)
    {
        in_order_tree(tree->left);
        printf("%d ",tree->key);
        in_order_tree(tree->right);
    }
}
void post_order_tree(bsTree tree)
{
    if(tree)
    {
        post_order_tree(tree->left);
        post_order_tree(tree->right);
        printf("%d ",tree->key);

    }
}

测试代码

我们插入值为1,5,4,3,2,10,6,11,12的结点

bsTree tree=NULL;
    int a[]= {1,5,4,3,2,10,6,11,12};


    int size=LENGTH(a);
    for(int i=0; i<size; i++)
    {
        tree=bstree_insert(tree,a[i]);
    }
    printf("前序遍历为:");
    pre_order_tree(tree);
    printf("\n");
    printf("中序遍历为:");
    in_order_tree(tree);
    printf("\n");
    printf("后序遍历为:");
    post_order_tree(tree);
    printf("\n");

    printf("删除10\n");

    bsNode *dele=create_node(10,NULL,NULL,NULL);
    tree=dele_node(tree,dele);
    printf("前序遍历为:");
    pre_order_tree(tree);
    printf("\n");
    printf("中序遍历为:");
    in_order_tree(tree);
    printf("\n");
    printf("后序遍历为:");
    post_order_tree(tree);
    printf("\n");

结果截图

《C语言实现二叉查找树》

    原文作者:二叉查找树
    原文地址: https://blog.csdn.net/silence2015/article/details/50954642
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞