数据结构与算法---二叉树基础

本文详细介绍了二叉树的概念,包括父节点、子节点、兄弟节点、根节点和叶子节点的定义。讨论了满二叉树和完全二叉树的特性,并讲解了如何使用链式存储和顺序储存来存储二叉树。重点阐述了二叉树的前序、中序和后序遍历,以及时间复杂度分析。此外,还提及了层序遍历的实现方法。文章最后给出了C++实现二叉树遍历的代码示例。
摘要由CSDN通过智能技术生成

1.树

在这里插入图片描述
在这里插入图片描述

  • 父节点: A节点是B节点的父节点
  • 子节点: B节点是A节点的子节点
  • 兄弟节点:B、C、D节点互为兄弟节点
  • 根节点: 没有父节点的节点叫做根节点
  • 叶子节点:没有子节点的节点,也叫叶节点

在这里插入图片描述
在这里插入图片描述
高度和深度相反,层数在深度+1

2.二叉树

  • 二叉树每个节点最多两个叉
  • 分别是左子节点和右子节点

2.1满二叉树

  • 叶子节点全都在最底层,
  • 除了叶子节点之外,每个节点都有左右两个子节点,
  • 这种二叉树就叫做满二叉树。
    在这里插入图片描述

2.2完全二叉树

  • 叶子节点都在最底下两层,
  • 最后一层的叶子节点都靠左排列,
  • 并且除了最后一层,其他层的节点个数都要达到最大,
  • 这种二叉树叫做完全二叉树。
    在这里插入图片描述

3. 如何存储一棵二叉树?

3.1 链式存储

在这里插入图片描述

  • 每个节点有三个字段,一个存数据,另外两个指向左右子节点的指针。
  • 大多数二叉树是通过这种结构来实现

3.2 顺序储存

在这里插入图片描述

  • 把下标i = 1 存在根节点
  • 左子节点存在 2*i = 2 的位置
  • 右子节点存在 2*i + 1 = 3 的位置
  • 依次类推,B的左子节点,在 2* i = 2 * 2 =4,右子节点 在5 的位置

总结:

  • 如果节点 X 存储在数组中下标为 i 的位置,
  • 下标为 2 * i 的位置存储的就是左子节点,
  • 下标为 2 * i + 1 的位置存储的就是右子节点。
  • 反过来,下标为 i/2 的位置存储就是它的父节点。
  • 如果是完全二叉树,数组存储最节省内存了
  • 堆其实就是一种完全二叉树,最常用的存储方式就是数组。

4.二叉树遍历

经典三种遍历:前序遍历中序遍历后序遍历
其中,前、中、后序,表示的是节点与它的左右子树节点遍历打印的先后顺序

  • 前序遍历是指,对于树中的任意节点来说,先打印这个节点,然后再打印它的左子树,最后打印它的右子树。
  • 中序遍历是指,对于树中的任意节点来说,先打印它的左子树,然后再打印它本身,最后打印它的右子树。
  • 后序遍历是指,对于树中的任意节点来说,先打印它的左子树,然后再打印它的右子树,最后打印这个节点本身。
    在这里插入图片描述

实际上,二叉树的前、中、后序遍历就是一个递归的过程。


前序遍历的递推公式:
preOrder(r) = print r->preOrder(r->left)->preOrder(r->right)

中序遍历的递推公式:
inOrder(r) = inOrder(r->left)->print r->inOrder(r->right)

后序遍历的递推公式:
postOrder(r) = postOrder(r->left)->postOrder(r->right)->print r

void preOrder(Node* root) {
  if (root == null) return;
  print root // 此处为伪代码,表示打印root节点
  preOrder(root->left);
  preOrder(root->right);
}

void inOrder(Node* root) {
  if (root == null) return;
  inOrder(root->left);
  print root // 此处为伪代码,表示打印root节点
  inOrder(root->right);
}

void postOrder(Node* root) {
  if (root == null) return;
  postOrder(root->left);
  postOrder(root->right);
  print root // 此处为伪代码,表示打印root节点
}

加粗样式

4.1 二叉树时间复杂度分析

从遍历图中可以看出,每个节点被访问两次 ,2n,时间复杂度O(n)

5. 思考题

  1. 给定一组数据,比如 1,3,5,6,9,10。你来算算,可以构建出多少种不同的二叉树?
    产生的总排列数就不是n!卡塔兰数
  2. 我们讲了三种二叉树的遍历方式,前、中、后序。实际上,还有另外一种遍历方式,也就是按层遍历,你知道如何实现吗?
    层序遍历,借用队列辅助即可,根节点先入队列,然后循环从队列中pop节点,将pop出来的节点的左子节点先入队列,右节点后入队列,依次循环,直到队列为空,遍历结束。
struct TreeNode {

    DataType data;    /* node data */

    struct TreeNode *leftPtr;   /* pointer to left subtree */

    struct TreeNode *rightPtr;  /* pointer to right subtree */
};
void FloorPrint_QUEUE(pTreeNode &Tree) //层序遍历_队列实现
{
    queue < pTreeNode> q;
    if (Tree != NULL)
    {
        q.push(Tree);   //根节点进队列
    }

    while (q.empty() == false)  //队列不为空判断
    {
        cout << q.front()->data << " → "; 

        if (q.front()->leftPtr != NULL)   //如果有左孩子,leftChild入队列
        {
            q.push(q.front()->leftPtr);   
        }

        if (q.front()->rightPtr != NULL)   //如果有右孩子,rightChild入队列
        {
            q.push(q.front()->rightPtr);
        }
        q.pop();  //已经遍历过的节点出队列
    }
}

6 .总结

1.两种特殊的二叉树
    1)满二叉树
    2)完全二叉树
2.二叉树定理
3.二叉树遍历
    1)递归
    2)迭代

满二叉树 --> 除叶子节点,其他节点都有两个非空子节点的二叉树
完全二叉树 --> 除最后一层,其余层都是满的


术语: 结点的度 --> 一个结点包含的子树个数
      树叶 --> 叶子节点,终端结点
      孩子结点 --> 相对于父节点(双亲结点)
      祖先结点 --> 从根结点到父节点都是
      子孙结点
      兄弟结点

      树的度 --> 使所有结点的度最大值(唯一性)

    >> 二叉树特点 --> 树的度 <= 2
                 --> 是有序树


二叉树性质
定理1 -->  若根结点的层数为1,在二叉树的i层至多有2(i-1)次方个结点
定理2 -->  k层二叉树最多有2(k)次方 - 1个结点
定理3 -->  n个结点的完全二叉树的层数为[log2N] + 1
定理4 -->  二叉树度数为2节点的个数等于叶节点个数减1

7.C++实现二叉树

typedef char BinTreeDataType;
//二叉树数据结构
typedef struct BTNode
{
    BinTreeDataType data;
   BTNode * lchild;
   BTNode * rchild;
}BTNode, *BinTree;


//层次建树,使用链表辅助
BinTree createBinTree(BinTree B, BinTreeDataType * cArr, int size);
//递归遍历
Status preOrder(BinTree B);
Status inOrder(BinTree B);
Status postOrder(BinTree B);
//非递归遍历
Status preOrderNot(BinTree B);
Status inOrderNot(BinTree B);
Status postOrderNot(BinTree B);
//层次遍历
Status sequenceOreder(BinTree B);
//层次建树,使用链表辅助
BinTree createBinTree(BinTree  B, BinTreeDataType * cArr, int size)
{
    if(0 >= size) return ERROR;
    int i;
    BinTree bArr = (BinTree)malloc(size * sizeof(BTNode));
    for(i = 0; i < size; i++)
    {
        bArr[i].data = cArr[i];
        bArr[i].lchild = NULL;
        bArr[i].rchild = NULL; 
    }
    LinkList L; //带头结点的链表
    createList(L, bArr, size);
    
    LNode * pFront, * pRear;
    pFront = L->next;
    pRear = L->next;
    B = NULL;
    while(NULL != pRear)
    {
        if(NULL == B)
        {
            B = &(pRear->data);
            pRear = pRear->next;
        }
        else if(NULL == (pFront->data).lchild)
        {
            (pFront->data).lchild = &(pRear->data);
            pRear = pRear->next; 
        }
        else if(NULL == (pFront->data).rchild)
        {
            (pFront->data).rchild = &(pRear->data);
            pRear = pRear->next;
        }
        else
        {
            pFront = pFront->next;
        }  
    }
    return B;
}
//递归遍历
Status preOrder(BinTree B)
{

    if(NULL == B) return ERROR;
    printf("%c ", B->data);
    preOrder(B->lchild);
    preOrder(B->rchild);
}
Status inOrder(BinTree B)
{

    if(NULL == B) return ERROR;
    inOrder(B->lchild);
    printf("%c ", B->data);
    inOrder(B->rchild);
}
Status postOrder(BinTree B)
{
    if(NULL == B) return ERROR;
    postOrder(B->lchild);
    postOrder(B->rchild);
    printf("%c ", B->data);
    
}
//非递归遍历 
//在入栈前打印
Status preOrderNot(BinTree B)
{
    BinTree root = B;
    BTNode temp;
    if(NULL == B) return ERROR;
    SqStack S;
    initStack(S, 1, 3);
    while(root || !emptyStack(S))
    {
        if(root)
        {
            printf("%c ", root->data);
            pushStack(S, *root);
            root = root->lchild;
            
        }
        else
        {
            popStack(S, temp);
            root = temp.rchild;
        }
    }
    return OK;
}
//在出栈后打印
Status inOrderNot(BinTree B)
{
    BinTree root = B;
    BTNode temp;
    if(NULL == B) return ERROR;
    SqStack S;
    initStack(S, 1, 3);
    while(root || !emptyStack(S))
    {
        if(root)
        {
            pushStack(S, *root);
            root = root->lchild;
            
        }
        else
        {
            popStack(S, temp);
            printf("%c ", temp.data);
            root = temp.rchild;
        }
    }
    return OK;
}
//使用两个栈实现
Status postOrderNot(BinTree B)
{
    BinTree root = B;
    BTNode temp, temp1;
    if(NULL == B) return ERROR;
    SqStack S, S1;
    initStack(S, 5, 5);
    initStack(S1, 5, 5);
    while(root || !emptyStack(S))
    {
        if(root)
        {
            pushStack(S, *root);
            root = root->lchild;
            
        }
        else
        {
            getTopStack(S, temp);
            getTopStack(S1, temp1);
            if(!emptyStack(S) && !emptyStack(S1) && temp1.data == temp.data)
            {
                popStack(S, temp);
                popStack(S1, temp1);
                printf("%c ", temp.data);
            }
            else
            {
                popStack(S, temp);
                if(NULL != temp.rchild) 
                {
                    pushStack(S, temp);
                    pushStack(S1, temp);
                }   
                else
                {
                    printf("%c ", temp.data);
                }
                root  = temp.rchild;
            }
        }
    }
    return OK;
}
//层次遍历
Status sequenceOreder(BinTree B)
{
    LinkList L;
    if(NULL == B) return OVERFLOW;
    enList(L, *B);
    LNode * pFront = L->next;
    BTNode * root = B;

    while(pFront)
    {
        if(pFront->data.lchild)
            enList(L, *(pFront->data.lchild));
        if(pFront->data.rchild)
            enList(L, *(pFront->data.rchild));
        pFront = pFront->next;
    }

    travelList(L);
    return OK;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值