树与二叉树比较常用的知识点小笔记

基本概念

  • 形式化定义(二元组)
    树:T={D,R}.数据集合与关系集合
    D是包含n个结点的有限集合(n>=0)
    n=0 空树,n>0有且仅有一个没有前驱的结点,称为root;其他结点只有一个前驱,但是可以有多个后继
  • 递归定义
    树可以是空树(无节点),或由以下部分组成:
  1. 一个根节点;
  2. 若干互不相交的子树​(每棵子树本身也是一棵树)。
    n=0 空树 n>0 根节点及m个不相交的子树构成

核心概念

  • 度:后继节点的个数
    度练习题
    1. Eg.
    一颗度为4的树T中,若有20个度为4的结点,10个度为3的结点,1个度为2的结点,10个度为1的节点,则树的叶子结点个数为()
    A.41 B.82 C.133 D.122

      度之和122
      需要加1=123
      减去度不为0的部分123-41=82
      选B
    
  • 分支节点:一个结点的度非零称为分支结点

  • 叶子节点:一个结点的度0 称为叶子结点

  • 边:连接相邻两个节点

  • 高度、深度

  • 路径:任意两个节点之间的边,路径唯一

  • n个结点,所有结点度之和是多少?等于边的个数n-1

  • d3:4,d2:5,d1:7,有多少个结点,多少个叶子结点

    1. 3 * 4+2 * 5+1 * 7=29 边 +1 = 30结点,4+5+7=16分支节点
    2. 30-16=14个叶子结点
  • 深度:从根结点(从0开始或者从1开始,不同资料不同开始,子节点层次递增

  • 高度:从结点到叶子结点路径中边的个数

  • 双亲结点:前驱结点 1

  • 子结点:后继结点 0~n

  • 兄弟结点:有公共双亲的结点

  • 非空二叉树上叶结点数等于双分支结点数加1,即:n0=n2+1

    1. 已知一颗完全二叉树的第6层(设根为第1层)有8个叶子结点,则该完全二叉树的结点个数最多是()
      A.39 B.52 C.111 D.119

节点:n
边: n-1
度之和:n-1

操作

  • 查找
  • 插入
  • 删除
  • 遍历
    - 先根(序)遍历
    - 后根
    - 层序
    - 中序(二叉树)

树的存储

  • 双亲存储 ‘ * ’ 顺序存储基于数组
  • 孩子链存储 指针个数过多
  • 孩子兄弟存储 ‘*’链式存储,基于指针

树的操作

  • 先序
  • 后序
  • 中序 ’ * ’
  • 层序
遍历方式访问顺序递归实现代码框架
前序遍历根→左→右visit(root); preOrder(left); preOrder(right);
中序遍历左→根→右preOrder(left); visit(root); preOrder(right);
后序遍历左→右→根preOrder(left); preOrder(right); visit(root);

=
![[Pasted image 20250328202552.png]]

先序:ABDHIEJKCFLMG
后序:HIDJKEBLMFGCA
中序:HDIBJEKALFMCG
层序:ABCDEFGHIJKLM

EG2.由后序中序推图
后序:GDBEFCA
中序:DGBAECF

![[Pasted image 20250328205448.png]]

二叉树性质

  • 二叉树与二次树有什么区别?

    1. 有限元素的集合,树,非空,由左子树和右子树构成
  • 满二叉树:分支结点的度都为2,叶子结点在最底下一层:2^n

  • 完全二叉树:分支结点的度都为2,叶子结点后两层:除了最底下一层,上面是满二叉树

  • 搜索二叉树

  • 哈夫曼树

  • 堆(优先级队列)

线索二叉树

具有n个结点的二叉树

二叉搜索树 BST

  • 左子树中的节点小于根节点
  • 右子树中的节点大于根节点

子树以下也是按照左小右大排序

  • 例题:给出数字13,9,42,3,5,1,19,72,32,7,4画二叉树
    ![[Pasted image 20250329193426.png]]

二叉搜索树的中序遍历即为从小到大排列

特殊:平衡二叉搜索树

  • AVL
  • 红黑树

n个结点
最坏 n层
最好log(N+1)

基于完全二叉树构成的堆

  • 最小堆(堆顶最小)
  • 最大堆(堆顶最大)

堆排序,优先级队列

存储结构:数组
二叉树

13,9,42,3,5,1,19,72,32
叶子结点n双亲就是(n-1)/2
如果将已经排好的堆堆顶拿走,拿最后一个叶子结点来补充,然后重新调整,避免完全二叉树的结构错乱
双亲 n 找孩子 左孩子2n+1 右孩子2n+2

![[Pasted image 20250329202353.png]]

示例
![[Pasted image 20250329203743.png]]

哈夫曼树

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

定长编码 ANSI 1byte 8bit,GB2312/GBK 2byte 16bit
变长编码 UTF-8 1byte, 3byte,

this is a line

2^8 = 256种
2^16 = 65536
t 1
h 1
i 3
s 2
a 1
l 1
n 1
e 1

哈夫曼树构造 选两个最小的树相加,结果再次放入树堆里,再次选两个小的,直到最后结束

叶子结点是分支结点+1

![[Pasted image 20250329210630.png]]

叶子结点是可以编码的

  • 规定哈夫曼树中的左分支为0,右分支为1,则从根结点到每个叶结点所经过的分支对应的0和1组成的序列便为该结点对应字符的编码。这样的编码称为哈夫曼编码。
  • 权值越大的字符编码越短,反之越长
    ![[Pasted image 20250329211315.png]]

![[Pasted image 20250329211401.png]]

** 哈夫曼树定义**

哈夫曼树(最优二叉树)是带权路径长度(WPL)最短的树,其中:

  • WPL:所有叶子节点权值 × 路径长度之和。
  • 构造方法:贪心算法,每次合并权值最小的两棵树。
构造步骤
  1. 统计频率:统计字符出现频率作为权值。
  2. 初始化森林:每个权值作为独立二叉树。
  3. 合并子树:重复合并最小两棵树,生成新父节点(权值为子节点和),直至只剩一棵树。

示例:权值集合 {5, 9, 12, 13, 16} 的构造过程:

合并5+9→14 → 合并12+13→25 → 合并14+16→30 → 合并25+30→55
最终树结构:根权值55,WPL=5×3 + 9×3 + 12×2 + 13×2 + 16×2 = 142
哈夫曼编码
  • 前缀编码:无歧义解码,左分支标记0,右分支标记1。
  • 编码生成:从根到叶子的路径即为二进制编码。

C语言实现哈夫曼树与编码**

** 数据结构定义**
typedef struct HuffmanNode {
    int weight;                 // 权值
    char data;                  // 字符(可选)
    struct HuffmanNode *left;   // 左孩子
    struct HuffmanNode *right;  // 右孩子
} HuffmanNode;
核心代码实现
// 创建哈夫曼树(基于最小堆)
HuffmanNode* buildHuffmanTree(char data[], int freq[], int size) {
    // 初始化最小堆(代码参考网页2)
    MinHeap* heap = createMinHeap(size);
    for (int i = 0; i < size; i++) {
        insertMinHeap(heap, createNode(data[i], freq[i]));
    }

    // 合并最小权值节点
    while (heap->size > 1) {
        HuffmanNode* left = extractMin(heap);
        HuffmanNode* right = extractMin(heap);
        HuffmanNode* parent = createNode('$', left->weight + right->weight);
        parent->left = left;
        parent->right = right;
        insertMinHeap(heap, parent);
    }
    return extractMin(heap);
}

// 生成哈夫曼编码表
void generateCodes(HuffmanNode* root, int codes[], int top) {
    if (root->left) {
        codes[top] = 0;
        generateCodes(root->left, codes, top + 1);
    }
    if (root->right) {
        codes[top] = 1;
        generateCodes(root->right, codes, top + 1);
    }
    if (!root->left && !root->right) {
        printf("%c: ", root->data);
        for (int i = 0; i < top; i++) printf("%d", codes[i]);
        printf("\n");
    }
}
应用示例

对字符串 “AFTERDATAEARARE” 进行编码:

  1. 统计频率:A(5), E(4), R(3), T(2), F(1), D(1)
  2. 构造哈夫曼树,生成编码:
    A: 0, E: 10, R: 110, T: 1110, F: 11110, D: 11111
    
  3. 原字符串压缩率提升至约 60%

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值