二叉树的创建和定义,递归和非递归的前序,中序和后序遍历。满二叉树和完全二叉树。二叉树的一些简单操作和应用。

二叉树的定义

我们先简单理解一下树,树就是N个结点的有限集。在任意一颗非空树中:有且仅有一个特定的称为根(Root)的结点。
二叉树是另一种树型结构,它的特点是每个结点至多只有两颗子树,所以二叉树中不存在度大于2的结点,并且,二叉树的子树有左右之分,其次序不能颠倒。两个结点共同的根节点叫做他们的双亲结点。
二叉树的结构体定义如下:

typedef char Data_Type;

typedef struct BTNode
{
    Data_Type data;
    struct BTNode * LeftNode;
    struct BTNode * RightNode;
}BTNode,*pBTNode;

所以二叉树每个结点都有一个data用来保存数据,还有两个指针,分别指向左右子树的根节点。
具体结构如下图:
《二叉树的创建和定义,递归和非递归的前序,中序和后序遍历。满二叉树和完全二叉树。二叉树的一些简单操作和应用。》

二叉树的创建

将一个字符串或者数组创建为二叉树形式,下面以字符串为例子:

void CreateBinTree(BTNode** pRoot,char* str,int size,int* index)
{
    BTNode* pNewNode = NULL;
    if(*index < size && '#'!=str[*index])
    {
        pNewNode = BuyBTNode();
        pNewNode->data = str[*index];
        *pRoot = pNewNode;
        (*index)++;
        CreateBinTree(&(*pRoot)->LeftNode,str,size,index);
        (*index)++;
        CreateBinTree(&(*pRoot)->RightNode,str,size,index);
    }
}
/////////////////////////
char str[]="ABD###CE##F";
pBTNode pRoot;
int index = 0;
CreateBinTree(&pRoot,str,11,&index);

上面的方法是字符串以递增的顺序传参,用递归的方法,以前序遍历的顺序(下面的代码会详细讲解前序遍历),将字符串中每一个字母添加到二叉树中去,”#“号是为了让增加停下,构造二叉树的具体形状。具体递归结束后二叉树如下图:
《二叉树的创建和定义,递归和非递归的前序,中序和后序遍历。满二叉树和完全二叉树。二叉树的一些简单操作和应用。》

二叉树的前序,中序和后序遍历

前序遍历就是像上面的代码一样,先对本次的结点进行操作,然后再传入左结点进行操作,左结点结束后再进行右结点的操作。
中序遍历就是先传入左结点,然后对本次结点进行操作,然后再传入右结点。
后序遍历就是先传入左结点,然后右结点,最后再对本次结点进行操作。
具体代码如下(操作以打印为例子):

void PreOrder(BTNode* pRoot)
{
    if(pRoot)
    {
        printf("%c ",pRoot->data);
        PreOrder(pRoot->LeftNode);
        PreOrder(pRoot->RightNode);
    }
}
void InOrder(BTNode* pRoot)
{
    if(pRoot)
    {
        InOrder(pRoot->LeftNode);
        printf("%c ",pRoot->data);
        InOrder(pRoot->RightNode);
    }
}
void PostOrder(BTNode* pRoot)
{
    if(pRoot)
    {
        PostOrder(pRoot->LeftNode);
        PostOrder(pRoot->RightNode);
        printf("%c ",pRoot->data);
    }
}

这是以递归的形式进行遍历,比较好理解。还有一种非递归的形式也可以实现前中后序遍历。
非递归就需要使用栈。
前序遍历:

void PreOrderNoR(BTNode* pRoot)
{
    Stack ps;
    BTNode* cur = pRoot;
    BTNode* top = NULL;
    assert(pRoot);
    StackInit(&ps);

    while(cur != NULL || !IsNull(&ps))
    {
        while(cur)
        {
            printf("%c ",cur->data);
            StackPush(&ps,cur);
            cur = cur->LeftNode;
        }
        top = StackTop(&ps);
        if(top->RightNode != NULL)
            cur = top->RightNode;
        StackPop(&ps);
    }

}

因为一开始就需要一直把二叉树每层左边的数据,从上到下的打印,所以先给
一个循环让结点一直往左结点遍历,直到下一个左结点为空,在这个过程中必须将之前走过的结点保存在栈里面,因为如果不保存就找不到之前结点。走到二叉树最左下的一个结点,然后取栈顶元素,也就是最后一个左结点,判断它是否有右结点,如果没有,就Pop取出栈顶的元素,进行下一次循环,而下一次循环cur依然为空,所以继续判断新的栈顶元素的右结点是否为空,直到循环停下的条件。如果有右结点,就将右结点赋值给cur,Pop取出本次的栈顶元素,(因为当下次操作结束时,取到的栈顶元素必须为,本次的双亲结点。)然后循环继续进行,cur作为刚才的结点的右结点继续进行遍历。具体顺序如绿线所示。(注意大循环的条件)
《二叉树的创建和定义,递归和非递归的前序,中序和后序遍历。满二叉树和完全二叉树。二叉树的一些简单操作和应用。》
非递归中序遍历如下:

void InOrderNorR(BTNode* pRoot)
{
    Stack ps;
    BTNode* cur = pRoot;
    BTNode* top = NULL;
    BTNode* pre = NULL;
    assert(pRoot);
    StackInit(&ps);
    while(cur!=NULL ||!IsNull(&ps))
    {
        while(cur)
        {
            StackPush(&ps,cur);
            cur = cur->LeftNode;
        }
        top = StackTop(&ps);
        printf("%c ",top->data);
        StackPop(&ps);
        if(top->RightNode != NULL)
            cur = top->RightNode;
    }
}

非递归后序遍历如下:

void PostOrderNoR(BTNode* pRoot)
{
    Stack ps;
    BTNode* cur = pRoot;
    BTNode* top = NULL;
    BTNode* pre = NULL;
    assert(pRoot);
    StackInit(&ps);
    while(cur!=NULL || !IsNull(&ps))
    {
        while(cur)
        {
            StackPush(&ps,cur);
            cur = cur->LeftNode;
        }
        top = StackTop(&ps);
        if(pre!=top->RightNode && top->RightNode != NULL)
        {
            cur = top->RightNode;
            continue;
        }
        else
        {
            printf("%c ",top->data);
            pre = top;
            StackPop(&ps);
        }
    }
}

满二叉树和完全二叉树

满二叉树的含义是二叉树每一层的结点都是满的比如说第一层有1,第二层有2,第三层有4,第四层有8个。。。以此类推,每一层都是满的。
完全二叉树是除了最后一层,上面的树是一颗满二叉树,并且最后一层的结点都是从左边开始依此排列的。如图:
《二叉树的创建和定义,递归和非递归的前序,中序和后序遍历。满二叉树和完全二叉树。二叉树的一些简单操作和应用。》
下面我们说一下如何用代码判断是否为二叉树。需要用到队列的储存结构。具体代码如下:

int IsCompleteBinTree(BTNode* pRoot)
{
    int flag = 1;
    QueueList qs;
    BTNode* cur = pRoot;
    QueueInit(&qs);
    QueuePush(&qs,cur);
    if(NULL == pRoot)
        return 1;
    else
    {
        while(flag && !QueueEmpty(&qs))
        {
            cur = QueueFront(&qs);
            if(cur->LeftNode && cur->RightNode)
            {
                QueuePush(&qs,cur->LeftNode);
                QueuePush(&qs,cur->RightNode);
            }
            else if(cur->LeftNode)
                flag = 0;
            else if(cur->RightNode)
                return 0;
            else
                flag = 0;
            QueuePop(&qs);
        }
        while(!QueueEmpty(&qs))
        {
            cur = QueueFront(&qs);
            if(NULL == cur->LeftNode && NULL == cur->RightNode)
                QueuePop(&qs);
            else
                return 0;
        }
        return 1;
    }
}

思路是要判断是否为完全二叉树,最关键的因素是,当二叉树自上而下,从左到右遍历的过程中,第一个碰到的只有左结点或者没有儿子节点的结点A时,此结点A之后的结点都必须是叶子结点,才符合完全二叉树,其他情况都不是。(而且一个二叉树中如果只有右结点没有左结点肯定不是完全二叉树。)所以我们需要做到,找到函数最早出现的没有左右两个孩子结点的结点。并且能保存此结点之后的所有结点。上面的代码就能做到,每次取队列的队头,判断它是否有左右孩子,然后如果有左右孩子说明不是我们要找的结点,保存它的左右孩子,然后将这个结点Pop出队列,然后再次取队头,取到的必然是刚才那个结点同层的下一个结点。并且可以继续保存它的左右孩子。然后找到第一个没有左右孩子的结点之后,就去下面的循环进行判断它之后的结点是否都为叶子结点。(在纸上画一下会非常的清晰)。

二叉树的N结点和K层问题

  • 若规定根节点的层数为1,则一颗非空二叉树的第i层上最多有2的(i-1)次方个结点。
  • 若规定只有根节点的二叉树的深度为1,则深度为K的二叉树的最大结点数是2^K-1(K>=0)
  • 对任何一颗二叉树,如果其叶结点个数为n0,度为2 的非叶结点个数为n2,则有n0=n2+1
  • 具有n个结点的完全二叉树的深度K为log2(n+1)上取整
    原文作者:满二叉树
    原文地址: https://blog.csdn.net/qq_39487033/article/details/82346942
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞