【数据结构复习总结】—— 树

👉 原文首发: 小牛肉的个人博客,欢迎来访~



一、思维导图

在这里插入图片描述

二、基本概念和性质

  1. 结点数等于所有结点的度数(边数)加1 v = e+1
  2. 对于一颗具有n个结点,度为4的树来说,树的最大高度为n-3;
    在这里插入图片描述
  3. 二叉树的性质:
    • n0 = n2 + 1; 叶子结点数数 = 度为2的结点+1
    • 高度为h的二叉树总共至多有 2的h次方 -1 个结点(满二叉树)
      第k层至多有 2的k-1次方 个结点;
    • 具有n个结点的完全二叉树的高度为 log2(n+1) 向上取整

三、二叉树的存储结构

顺序存储

顺序存储(数组):空间利用率较低

链式存储

链式存储(链表):左右孩子指针(含有n个结点的二叉链表中,含有n+1个空链域)

  • C
typedef struct BiTNode{
    int data;
    struct BiTNode* lchild,*rchild;
}BiTNode,*BiTree;
  • C++
template<class T>
struct BiTNode{
    T data;
    BiTNode<T> *lchild,*rchild;
    BiTNode():lchild(NULL),rchild(NULL){} //无参构造
    BiTNode(T x, BiTNode<T> *l = NULL, BiTNode<T> *r = NULL):data(x),lchild(l),rchild(r){} //带参构造
};

四、二叉树的三种遍历+层次遍历

以下代码给出三种遍历的递归和非递归实现,均为 c++ 实现

先中后遍历均借助栈实现,层次遍历借助队列实现

先序遍历

  • 递归
public static void scan(BiTree b) {
    cout<<r->data;
    if(b->lchild != null) scan(b->lchild);
    if(b->rchild != null) scan(b->rchild);
}
  • 非递归
//先序遍历非递归
void InOrder(BiTree T){
    BiTree p = T;
    while(p || !is_empty(S)){
        if(p){
            visit(p->data);
            push(S,p);
            p=p->lchild;
        }
        else{
            pop(S,p);
            p = p->rchild
        }
    }
}

中序遍历

  • 递归
public static void scan(BiTree b) {
    if(b != null) {
   		scan(b->lchild);
        cout<<r->data;
        scan(b->rchild);
    }
}
  • 非递归
//中序遍历非递归
void InOrder(BiTree T){
    BiTree p = T;
    while(p || !is_empty(S)){
        if(p){
            push(S,p);
            p=p->lchild;
        }
        else{
            pop(S,p);
            visit(p->data);
            p = p->rchild
        }
    }
}

后序遍历

  • 递归
public static void scan(BiTree b) {
    if(b != null) {
   		scan(b->lchild);
        scan(b->rchild);
        cout<<r->data;
    }
}
  • 非递归
//后序遍历非递归
void PostOrder(BiTree T){
    InitStack(S);
    p = T;
    r = NULL; //最近访问过的结点
    while(p || !is_empty(S)){
        if(p){
            Push(S,p);
            p = p->lchild;
        }
        else{
            GetTop(S,p);
            //右孩子存在且未被访问过
            if(p->rchild && r!=p->rchild){
                p = p->rchild;
                Push(S,p);
                p = p->lchild; //再走到最左
            }
            else{ //若既无左孩子也无右孩子,则弹出结点并访问
                Pop(S,p); 
                visit(p->data);
                r = p; //置为最近访问结点
                p = NULL; //p重置
            }
        }
    }
}

层次遍历

void LevelOrder(BiTree T){
    BiTNode p;
    EnQueue(Q,T);
    while(!isEmpty(Q)){
        DeQueue(p);
        visit(p);
        if(p->lchild!=NULL)
            EnQueue(Q,p->lchild);
        if(p->rchild!=NULL)
            EnQueue(Q,p->rchild);
    }
}

若已知以下三种序列组合,则可以唯一的确定一颗二叉树

  • 先序和中序序列(前序为进栈序列,中序为出栈序列)
  • 后序和中序序列
  • 层次和中序序列

五、二叉排序树BST / 二叉查找树

定义

二叉排序树又称二叉查找树,它或者是一颗空树,或者满足一下性质的二叉树:

  • 若左子树不空,则左子树上所有结点的值均小于根节点的值;
  • 若右子树不空,则右子树上所有结点的值均大于根节点的值;
  • 左右子树也分别是二叉排序树。

递归的数据结构,左<根<右 ,中序遍历为递增序列

插入和删除操作

1.插入
插入的结点一定是某个叶结点

2.删除

  • 被删除结点时叶子结点,直接删除;
  • 被删除结点z只有一棵左子树或者右子树,则删除z,令z的子树成为z父结点的子树
    在这里插入图片描述
  • 被删除结点z有两棵子树,则交换z的 直接后继(中序遍历) 和z的位置,转换为第二种情况删除z
    在这里插入图片描述

3.查找效率分析

  • 最好情况:平衡二叉树 O(log2n)
    在这里插入图片描述
  • 最坏情况:输入序列有序 O(n)
    在这里插入图片描述

二叉排序树的判定

思路:二叉排序树的中序遍历序列一定为递增序列
代码:

//利用中序遍历为递增
bool isBST(BiTree T){
    if(T==NULL)
        return true;
    else{
        bool bl = isBST(T->lchild);
        if(bl==false || pre>T->data)
            return false;
        pre = T->data;
        bool br = isBST(T->rchild);
        return br;
    }
}

六、平衡二叉树AVL

定义

为避免树的高度增长过快,降低二叉排序树的性能,
规定插入和删除时 任意结点的左右子树高度差的绝对值不超过1

平衡二叉树是特殊的二叉排序树

插入操作(每次调整对象都是最小不平衡子树)

  1. LL右单旋:(在结点A的左孩子的左子树插入新结点导致A失衡)
    在这里插入图片描述
  2. RR左单旋:(在结点A的右孩子的右子树插入新结点导致A失衡)
    在这里插入图片描述
  3. LR左右双旋:(在结点A的左孩子的右子树插入新结点导致A失衡)
    在这里插入图片描述
  4. RL右左双旋:(在结点A的右孩子的左子树插入新结点导致A失衡)
    在这里插入图片描述

平衡二叉树的判定

思路:平衡二叉树的每一个结点都是平衡的,利用后序遍历按照左右根的次序依次判断是否平衡二叉树

int isAVL(BiTree T,int &h,int &balance){
    int bl,br,hl,hr; //左右子树的高度和平衡性
    if(T==NULL){
        balance = 1;
        h = 0;
    }
    else if(!T->lchild && !T->rchild){ //左右孩子均不存在
        balance = 1;
        h = 1;
    }
    else{
        isAVL(T->lchild,hl,bl); //判定左子树
        isAVL(T->rchild,hr,br); //判定右子树
        h = hl>hr?hl:hr;
        if(bl&&br && abs(hl-hr)<2) //若左右子树均平衡且高度差小于等于1
            balance = 1;
        else
            balance = 0;
    }
}

完全二叉树的判定

思路: 完全二叉树的结点编码是连续的,根据层次遍历,把所有结点依次入队,包括空结点,若空结点之后还有非空结点,就不是完全二叉树

代码:

bool isComplete(BiTree T){
    if(!T)
        return true;
    InitQueue(Q);
    BiTNode *p = T;
    EnQueue(Q,T);
    while(!isEmpty(Q)){
        DeQueue(Q,p);
        if(p){
            EnQueue(Q,p->lchild);
            EnQueue(Q,p->rchild);
        }
        else{
            while(!isEmpty(Q)){
                DeQueue(Q,p);
                if(p)
                    return false;
            }
        }
    }
    return true;
}

七、哈夫曼树(最优二叉树)和哈夫曼编码

定义

给定n个权值作为n个叶子结点,构造一棵二叉树,若带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman tree)

构造方法

每次选取两棵根结点权值最小的树作为新结点的左右子树

在这里插入图片描述

哈夫曼编码

前缀编码:没有一个编码是另一个编码的前缀,比如:01 011 010 就不是前缀编码,因为01是011的前缀,根据01不能唯一确定用哪个编码。

哈夫曼编码就是长度最短的前缀编码,减少编码的长度。

八、树、森林、二叉树的转换

树转化为二叉树

二叉树中遵循 左孩子右兄弟 的原则
在这里插入图片描述

森林转化为二叉树

  • 将第一棵树的根作为转换后的二叉树的根;
  • 将第一棵左子树作为转换后二叉树根的左子树;
  • 将第二棵树作为转化后二叉树的右子树……….

在这里插入图片描述

二叉树转化为森林

  • 二叉树的根及其左子树作为第一棵树;
  • 右子树作为第二棵树;
  • 直到产生一棵没有右子树的二叉树为止
    在这里插入图片描述
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

飞天小牛肉

您的鼓励是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值