第五章 树与二叉树

第五章 树与二叉树

基础概念知识

结点的度:结点的孩子个数

树的度:树中结点的最大度数

分支节点:度>0

叶子结点:度=0

结点的层次:根结点为第1层,往下以此类推

结点的深度:从根结点开自顶向下逐层累加

结点的高度:从叶结点开始自底向上逐层累加

树的高度(或深度):树中结点的最大层数

有序树:树中结点的各子树从左到右是有次序的;否则称为无序树

路径:两个结点之间所经过的节点序列构成

路径长度:所经过的边的个数(树的分支有向,从上向下)

森林:m棵互不相交的树的集合

树的性质

  1. 结点数 = 所有结点的度数之和 + 1

  2. 度为m的树中第i层至多有 m i − 1 m^{i-1} mi1个结点

  3. 高度为h的m叉树至多有 ( m h − 1 ) / ( m − 1 ) (m^h-1)/(m-1) (mh1)/(m1);( m 0 + m 1 + m 1 + . . . + m h = ( m h − 1 ) / m − 1 m^0+m^1+m^1+...+m^h = (m^h-1)/m-1 m0+m1+m1+...+mh=(mh1)/m1

  4. 具有n个结点的m叉树的最小高度为 ⌈ l o g m ( n ( m − 1 ) ) + 1 ⌉ \lceil log_m(n(m-1))+1 \rceil logm(n(m1))+1

    具有n个结点的m叉树的最大高度为 n − m + 1 n-m+1 nm+1

满二叉树:高度为h,且含有 2 h − 1 2^h-1 2h1个结点的二叉树

  1. 编号为 i i i 的结点的第 1 个子女结点(若存在)的编号: ( i − 1 ) ∗ m + 2 (i-1)*m+2 (i1)m+2

    编号为 i i i 的结点的 k k k 个子女结点(若存在)的编号 ( i − 1 ) ∗ m + k + 1 (i-1)*m+k+1 (i1)m+k+1

  2. 编号为 i i i 的结点的双亲结点(若存在)的编号 ⌊ ( i − 2 ) / m ⌋ + 1 \lfloor (i-2)/m \rfloor + 1 (i2)/m+1

  3. 编号为 i i i 的结点有右兄弟的条件 i ≤ ⌊ ( i + m − 2 ) / m ⌋ ∗ m i \leq \lfloor (i+m-2)/m \rfloor *m i(i+m2)/mm;且右兄弟编号为 i + 1 i+1 i+1

    结点 i i i 不是其双亲的第 m m m 个子女时才有右兄弟。

完全二叉树:高度为h,有n个结点的二叉树,当且仅当其每个结点都与高度为h的满二叉树中的编号1~n一一对应

完全二叉树特点

  1. i ≤ ⌊ n / 2 ⌋ i\leq \lfloor n/2 \rfloor in/2,则结点 i i i 为分支节点,否则为叶子节点

  2. 叶子结点只可能在层次最大的两层出现

  3. 若有度为1的结点,则只可能有一个,且为左孩子; n 1 = 0 或 1 n_1 = 0或1 n1=01

    若有2k(偶数)个结点, n 1 = 1 ; n 0 = k , n 2 = k − 1 n_1 = 1;n_0 = k,n_2 = k-1 n1=1;n0=k,n2=k1

    若有2k-1(奇数)个结点, n 1 = 0 ; n 0 = k , n 2 = k − 1 n_1 = 0;n_0 = k,n_2 = k-1 n1=0;n0=k,n2=k1

  4. 按层序编号,一旦出现某结点 i i i 为叶子结点,则编号大于 i i i 的结点均为叶子结点

二叉排序树:左子树<根<右子树

平衡二叉树:任一结点的左右子树深度差<=1

二叉树的性质

  1. 叶子结点数 = 度为2的结点数+1; n 0 = n 2 + 1 n_0 = n_2 + 1 n0=n2+1
    { n = n 0 + n 1 + n 2 n = n 1 + 2 n 2 + 1 ⟹ n 0 = n 2 + 1 \left\{ \begin{matrix} n = n_0 + n_1 + n_2\\ n = n_1 + 2n_2 + 1\\ \end{matrix} \right. \Longrightarrow n_0 = n_2 + 1 {n=n0+n1+n2n=n1+2n2+1n0=n2+1

  2. 非空二叉树上第k层上至多有 2 k − 1 2^{k-1} 2k1个结点

  3. 高度为h的二叉树至多有 2 h − 1 2^{h}-1 2h1个结点

  4. 完全二叉树结点 i i i 所在的层次(深度)为 ⌊ l o g 2 i ⌋ + 1 \lfloor log_2i \rfloor + 1 log2i+1

  5. 具有n个结点的完全二叉树的高度为 ⌈ l o g 2 ( n + 1 ) ⌉ \lceil log_2(n+1) \rceil log2(n+1) ⌊ l o g 2 n ⌋ + 1 \lfloor log_2n \rfloor+1 log2n+1
    2 h − 1 − 1 < n < 2 h − 1 或 2 h − 1 < n < 2 h ⟹ 2 h − 1 < n + 1 < 2 h 或 h − 1 < l o g 2 ( n + 1 ) < h ⟹ h = ⌈ l o g 2 ( n + 1 ) ⌉ 或 h − 1 < l o g 2 n < h h = ⌊ l o g 2 n ⌋ + 1 2^{h-1}-1<n<2^h-1 或 2^{h-1}<n<2^{h}\\ \Longrightarrow 2^{h-1}<n+1<2^h 或 h-1<log_2(n+1)<h\\ \Longrightarrow h = \lceil log_2(n+1) \rceil 或 h-1 < log_2n<h\\ h = \lfloor log_2n \rfloor + 1 2h11<n<2h12h1<n<2h2h1<n+1<2hh1<log2(n+1)<hh=log2(n+1)h1<log2n<hh=log2n+1

n个结点的二叉链表中,含有n+1个空指针域

先序序列二叉树的个数 1 n + 1 C 2 n n \dfrac{1}{n+1}C_{2n}^{n} n+11C2nn

二叉树遍历(C++)

指路leetcode,可自行练习

前序遍历 leetcode144

迭代法
//前序遍历 leetcode144
//迭代法
class Solution {
public:
    vector<int> a;
    vector<int> preorderTraversal(TreeNode* root) {
        
        TreeNode* p = root;
        stack<TreeNode*> s;
        while(p || !s.empty()){
            while(p){
                a.push_back(p->val);
                s.push(p);
                p = p->left;
            }
            p = s.top();
            s.pop();
            p = p->right;
        }
        return a;
    }
};

递归法
//递归法
class Solution {
public:
    vector<int> a;
    vector<int> preorderTraversal(TreeNode* root) {
        if(root == NULL) return a;
        a.push_back(root->val);
        preorderTraversal(root->left);
        preorderTraversal(root->right);
        return a;
    }
};

中序遍历 leetcode94

迭代法
//中序遍历 leetcode94
//迭代法
class Solution {
public:
    vector<int> a;  
    vector<int> inorderTraversal(TreeNode* root) {
        TreeNode*p =root;
        stack<TreeNode*> s;
        while(p || !s.empty()){
            while(p){
                s.push(p);
                p = p->left;
            }
            p = s.top();
            s.pop();
            a.push_back(p->val);
            p = p->right;
        }
        return a;
    }
};

递归法
//递归法
class Solution {
public:
    vector<int> a;  
    vector<int> inorderTraversal(TreeNode* root) {
        if(root == NULL)return a;
        inorderTraversal(root->left);
        a.push_back(root->val);
        inorderTraversal(root->right);
        return a;
    }
};

后序遍历 leetcode145

迭代法
//后序遍历 leetcode145
//迭代法
class Solution {
public:
    vector<int> a;
    vector<int> postorderTraversal(TreeNode* root) {
        if (root == nullptr) {
            return a;
        }
        stack<TreeNode*> s;
        TreeNode* p = root;
        TreeNode* r = nullptr;
        while(p || !s.empty()){
            while(p){
                s.push(p);
                p = p->left;
            }
            p = s.top();
            s.pop();
            if(p->right == nullptr || p->right == r){//当p的右子树为空 或 p的右子树已经被访问过
                a.push_back(p->val);
                r = p;
                p = nullptr;
            }else{
                s.push(p);
                p = p->right;
            }
        }
        return a;
    }
};

递归法
//递归法
class Solution {
public:
    vector<int> a;  
    vector<int> inorderTraversal(TreeNode* root) {
        if(root == NULL)return a;
        inorderTraversal(root->left);
        inorderTraversal(root->right);
        a.push_back(root->val);
        return a;
    }
};

层序遍历 leetcode102

//层序遍历 leetcode102
class Solution {
public:
    vector<vector<int>> a;
    vector<vector<int>> levelOrder(TreeNode* root) {
        if(root == NULL) return a;
        queue<TreeNode*> q;
        TreeNode * p = root;
        q.push(p);
        while(!q.empty()){
            int n = q.size();
            a.push_back(vector<int>());
            for(int i = 0;i < n;i++){
                p = q.front();
                q.pop();
                a.back().push_back(p->val);
                if(p->left != NULL)q.push(p->left);
                if(p->right != NULL)q.push(p->right);
            }

            
        }
        return a;
    }
};

中序和另外三种任意一种序列组合,都可以唯一确定一棵二叉树。

完全二叉树(Java)

//is complete binary tree
public static boolean isCBT(Node head) {

    if(head == null) {
        return true;
    }
    Queue<Node> queue = new LinkedList<>();
    boolean leaf = false;
    Node leftN = null;
    Node rightN = null;
    queue.offer(head);
    while (!queue.isEmpty()) {

        head = queue.poll();
        leftN = head.left;
        rightN = head.right;
        if((leftN == null && rightN != null) || (leaf && (leftN != null || rightN != null))) {
            return false;
        }
        if(leftN != null) {
            queue.offer(leftN);
        }
        if(rightN != null) {
            queue.offer(rightN);
        } else {
            leaf = true;//叶子节点开始标志,若为“完全二叉树”则之后的待处理的节点都应为叶子节点
        }
    }

    return true;
}

算法解析:

1.按层遍历二叉树,从每层的左边向右边依次遍历;

2.如果当前节点有右孩子,但没有左孩子,直接返回false;

3.如果当前节点并不是左右孩子都有,那之后的节点应都为叶节点,否则返回false;

4.设置初始标志位true, 如果遍历结束没有返回false,则为完全二叉树,返回最终结果true。

二叉搜索树

左子树 < 根 < 右子树

删除操作:

  1. 被删除结点 z 为叶子节点,直接删除
  2. 结点z只有一颗左子树或右子树,让 z 的子树成为 z 父结点的子树,替代 z 的位置。
  3. 若结点 z 有左、右两棵子树,则令 z 的直接后继(或直接前驱)替代 z ,然后从二叉排序树中删去这个直接后继(或直接前驱),这样就转换成了第一或第二种情况。

二叉平衡树

平衡调整

  1. LL平衡旋转(右单旋转)----左孩子的左子树

    在这里插入图片描述

    f->lchild = p->rchild
    p->rchild = f
    gf->lchild/rchild = p
    
  2. RR平衡旋转(左单旋转)----右孩子的右子树

    f->lchild = p->rchild
    p->rchild = f
    gf->lchild/rchild = p
    
  3. LR平衡旋转(先左后右双旋转)----左孩子的右子树

  4. RL平衡旋转(先右后左双旋转)----右孩子的左子树

n h n_h nh表示深度为h的平衡树中含有最少结点数。 n 0 = 0 n_0 = 0 n0=0 n 1 = 1 n_1 = 1 n1=1 n 2 = 2 n_2 = 2 n2=2,并且有 n h = n h − 1 + n h − 2 + 1 n_h = n_{h-1}+n_{h-2}+1 nh=nh1+nh2+1

最大深度为 O ( l o g 2 n ) O(log_2n) O(log2n),即平均查找长度为 O ( l o g 2 n ) O(log_2n) O(log2n)

霍夫曼树

编码压缩方法;霍夫曼编码;霍夫曼树创建方法;

结点的带权路径长度:从树根到任意结点的路径长度(经过的边数)与该节点上权值的乘积

树的带权路径长度:树根中所有叶结点的带权路径长度之和
W P L = ∑ i = 1 n w i l i WPL = \sum_{i=1}^{n}w_il_i WPL=i=1nwili
其中, w i w_i wi是第 i i i 个叶结点所带的权值, l i l_i li 是该叶结点到根节点的路径长度

带权路径长度(WPL)最小的二叉树称为哈夫曼树,也称最优二叉树。

哈夫曼树特点:

  1. 每个初始结点最终都成为叶结点,且权值越小的结点到根节点的路径长度越大
  2. 构建过程共新建了n-1个结点(双分支节点),因此哈夫曼树的节点总数为2n-1
  3. 每次构造都选择2棵树作为新结点的孩子,因此哈夫曼树中不存在度为1的结点

哈夫曼编码:数据压缩编码

固定长度编码:每个字符用相等长度的二进制位表示

可变长度编码:允许对不同字符用不等长的二进制位表示

对高频字符用短编码,对低频字符用长编码

前缀编码:没有一个编码是另一个编码的前缀

由哈夫曼树构造哈夫曼编码:

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值