二叉树的一些基本操作


    转眼,学习编程已经快4个月了。虽然目前只掌握了C的一些基本语法,也只阅读了《数据结构与算法分析》的线性结构和树的部分,但起码我在自己拙劣的代码成功运行时能感受到快乐和成就感。我现在是大一,我想我会一直坚持学习下去。我希望在多年后,再次回首看自己写的博客时,能够高兴地看到自己的进步。

    我决定写我人生中的这第一个博客有两个目的:1.加强对知识系统的理解和复习,毕竟这是一个很好的方式。通过发博客发代码的形式,我想我能更好地掌握那些我所迷惑的问题。2.我希望能提高我的表述能力,因为我觉得能够将自己对知识的理解很好的表述出来是很重要的,这也考验了我到底学的怎么样。

    第一次的博客来写二叉树,是因为学习到这里的时候,我多多少少遇到了一些阻力。而这些阻力是学习前面那些线性结构时所没有遇到的。可能是因为这是我所接触到的第一个二维的结构吧,而且这里面的递归运用也很多。当然这不能称为困难的地方,因为目前为止我还没有掌握树的一些非递归操作。所以我准备在下一篇博客中尽我所能将树的一些非递归操作写出来。


1.二叉树的定义

<span style="font-family:Microsoft YaHei;"><span style="font-family:Courier New;font-size:12px;">typedef int ItemType
struct TreeNode{
    ItemType Item;
    struct TreeNode * Left;
    struct TreeNode * Right;
};
typedef struct TreeNode * PtrToNode;
typedef PtrToNode SearchTree;
typedef PtrToNode Position;</span>
</span>


2.二叉树的创建、插入与删除

1)创建

<span style="font-family:Courier New;font-size:12px;">SearchTree MakeEmpty(SearchTree T)
{
    return NULL;
}</span>

2)插入

<span style="font-family:Microsoft YaHei;"><span style="font-family:Courier New;font-size:12px;">SearchTree Insert(ItemType X, SearchTree T)
{
    if (T == NULL)
    {
        T = (PtrToNode)malloc(sizeof(struct TreeNode));
        if (T == NULL)
            exit(1);

        T->Item = X;
        T->Left = T->Right = NULL;
    }
    
    else if (X > T->Item)//递归插入右子树
        Insert(X, T->Right);

    else if (X < T->Item)//递归插入左子树
        Insert(X, T->Left);

        return T;        //若X已在树中,则直接返回原树
}</span>
</span>

3)删除制定项

    值得一提的删除操作是删除左右子树都不为空的节点这种情况。此时的核心思想是:找到待删除节点右子树的最小值替换掉待删除节点,再递归删除此最小值节点。

    但此种删除方法的缺点是,有一种使左子树越来越重,而右子树越来越轻的趋势。

<span style="font-family:Microsoft YaHei;"><span style="font-family:Courier New;font-size:12px;">SearchTree Delete(ItemType X, SearchTree T)
{
    Position P;
    if (T == NULL)
    {
        puts("Item not found");
        exit(1);
    }
    else if (X > T->Item)         //注意到函数的定义方式,只传递了一个指向节点的指针副本,
        T->Right = (X, T->Right); //所以要将改变后的树节点指针传递给原来的节点。下同。
    else if (X < T->Item)   
        T->Left = (X, T->Left);
    else if (T->Left && T->Right) //已经找到节点,且这是节点左右子树都不为空的情况
    {
        P = FindMin(T);
        T->Item = P->Item;
        T->Right = Delete(P->Item, T); //递归删除右子树的最小节点
    }
    else    //这里处理剩下的三种情况
    {
        P = T;
        if (T->Left == NULL)  //这里也处理了左右子树都为空的情况
            T = T->Right;
        else if (T->Right == NULL)
            T = T->Left;
        free(P);
    }

    return T;
}

//此方法需用到的查找树最小节点的函数
static SearchTree FindMin(SearchTree T)
{
    if (T == NULL)
        return T;
    else if (T->Left == NULL)
        return T;
    else
        return FindMin(T->Left);
}</span>
</span>

3.二叉树的遍历

    利用递归可以很便利、简洁地实现遍历。具体过程也无需赘言,在此将前中后一并贴出。

<span style="font-family:Courier New;font-size:12px;">void PreOrderTraversal(SearchTree T, void(*pfun)(ItemType X))
{
    if (T != NULL)
    {
        (*pfun)(T->Item);
        PreOrderTraversal(T->Left, pfun);
        PreOrderTraversal(T->Right, pfun);
    }
}

void InOrderTraversal(SearchTree T, void(*pfun)(ItemType X))
{
    if (T != NULL)
    {
        InOrderTraversal(T->Left, pfun);
        (*pfun)(T->Item);
        InOrderTraversal(T->Right, pfun);
    }
}

void PostOrderTraversal(SearchTree T, void(*pfun)(ItemType X))
{
    if (T != NULL)
    {
        PostOrderTraversal(T->Left, pfun);
        PostOrderTraversal(T->Right, pfun);
        (*pfun)(T->Item);
    }
}</span>

    想了想,还是把AVL树的插入操作也贴上吧。后面的博客会贴上非递归版本。


4.AVL树的递归插入

    核心思想是:当递归插入到适当的位置时,沿插入路径挨个检查父辈节点的左右子树高度差(这里利用了节点中事先储存好的“高度”信息,储存"高度“信息的操作也在插入操作中进行),如果等于2则说明需要旋转来平衡高度。接下来,判断是应该进行single rotation 还是double rotation ,最后执行封装入相应函数的旋转操作。具体实现见代码。

//树的定义
typedef int ItemType;
struct AvlNode{
    ItemType Item;
    struct AvlNode * Left;
    struct AvlNode * Right;
    int Height;
};
typedef struct AvlTree * PtrToNode;
typedef PtrToNode AvlTree;
typedef PtrToNode Position;

//插入操作
AvlTree Insert(ItemType X, AvlTree T)
{
    if (T == NULL)
    {
        T = (PtrToNode)malloc(sizeof(struct AvlNode));
        if (T == NULL)
            exit(1);
        T->Item = X;
        T->Left = T->Right = NULL;
        T->Height = 0;
    }

    else if (X > T->Item)
    {
        T->Right = Insert(X, T->Right);
        if (Height(T->Right) - Height(T->Left) == 2)
        {
            if (X > T->Right->Item)//执行单旋
                T = RightSingleRotation(T);
            else                   //执行双旋
                T = RightDoubleRotation(T);
        }
    }

    else if (X < T->Item)
    {
        T->Left = Insert(X, T->Left);
        if ((Height(T->Left) - Height(T->Right)) == 2)
        {
            if (X < T->Left->Item)//执行单旋
                T = LeftSingleRotation(T);
            else                 //执行双旋
                T = LeftDoubleRotation(T);
        }
    }

    //计算并储存高度
    T->Height = (T->Left->Height > T->Right->Height) ?
        (T->Left->Height + 1) :
        (T->Right->Height + 1);

    return T;
}

//单旋及双旋函数各贴一个,另一种对称旋转与其类似。
static AvlTree RightSingleRotation(AvlTree T)
{
    Position X = T->Right->Left;
    AvlTree NewT = T->Right;
    NewT->Left = T;
    T->Right = X;
    //不要忘了更新高度
    T->Height = (T->Left->Height > T->Right->Height) ?
        (T->Left->Height + 1) :
        (T->Right->Height + 1);
    NewT->Height = (NewT->Left->Height > NewT->Right->Height) ?
        (NewT->Left->Height + 1) :
        (NewT->Right->Height + 1);

    return NewT;
}

static AvlTree RightDoubleRotation(AvlTree T)
{
    T->Right = LeftSingleRotation(T->Right);

    return RightSingleRotation(T);
}

//Height函数
static Height(Position P)
{
    if (P == NULL)
        return -1;
    else
        return P->Height;
}
    啊!花了两个晚上,每晚两个小时左右,终于写完这篇博客了。

    最后在这里默默地吐槽,其实我每天想花更多的时间学习编程的,可无奈破学校破事水课都太多了,还有无意义的点名的晚自习。所以还是抓紧一切可用时间学吧。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值