G
N
I
D
A
O
L

数据结构-树-概念(一)

概念和特点

  • 概念:
    树(英语:tree)是一种抽象数据类型(ADT)或是实现这种抽象数据类型的数据结构,用来模拟具有树状结构性质的数据集合。它是由n(n>0)个有限节点组成一个具有层次关系的集合。把它叫做“树”是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。
  • 特点:
  1. 每个节点都只有有限个子节点或无节点;
  2. 没有父节点的节点称为根节点;
  3. 每一个非根节点有且只有一个父节点;
  4. 除了根节点外,每个节点可以分为多个不相交的子树;
  5. 树里面没有环路(cycle,类似图,节点间交互)

术语

  1. 节点的度:一个节点含有的子树的个数称为该节点的度(即是度为:0,1或2);
  2. 树的度:一棵树中,最大的节点度称为树的度;
  3. 叶节点或终端节点:度为零的节点;
  4. 非终端节点或分支节点:度不为零的节点;
  5. 父亲节点或父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点;
  6. 孩子节点或子节点:一个节点含有的子树的根节点称为该节点的子节点;
  7. 兄弟节点:具有相同父节点的节点互称为兄弟节点;
  8. 节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推;
  9. 深度:对于任意节点n,n的深度为从根到n的唯一路径长,根的深度为0;
  10. 高度:对于任意节点n,n的高度为从n到一片树叶的最长路径长,所有树叶的高度为0;
  11. 堂兄弟节点:父节点在同一层的节点互为堂兄弟;
  12. 节点的祖先:从根到该节点所经分支上的所有节点;
  13. 子孙:以某节点为根的子树中任一节点都称为该节点的子孙。
  14. 森林:由m(m>=0)棵互不相交的树的集合称为森林;

节点维护信息

rt tot fa[i] ch[i][0/1] val[i] cnt[i] sz[i]
根结点编号 节点个数 父亲 左右儿子编号 节点权值 权值出现次数 子树大小

操作

旋转操作

树旋转(Tree rotation)是对二叉树的一种操作,不影响元素的顺序,但会改变树的结构,将一个节点上移、一个节点下移。树旋转会改变树的形状,因此常被用来将较小的子树下移、较大的子树上移,从而降低树的高度、提升许多树操作的效率。

树的旋转方向有很多不同的定义,有些定义彼此之间还存在冲突。有些人认为旋转方向应该反映节点的移动方向(左子树旋转到父节点的位置为右旋),有些人则认为旋转方向应该反映被旋转的子树是哪棵(左子树旋转到父节点的位置为左旋,与前一种说法相反)。普通接受的定义:旋转方向为节点的移动方向(前者)

  • 旋转要求:
    • 整棵树旋转前后的中序遍历不变;
    • 受影响的节点维护的信息依然正确有效;
    • root必须指向旋转后的根节点

树旋转操作

  • 操作步骤:

    • 将 y 的左儿子指向 x 的右儿子,且 x 的右儿子(如果 x 有右儿子的话)的父亲指向 y;ch[y][0]=ch[x][1]; fa[ch[x][1]]=y;
    • 将 x 的右儿子指向 y,且 y 的父亲指向 x;ch[x][chk^1]=y; fa[y]=x;
    • 如果原来的 y 还有父亲 z,那么把 z 的某个儿子(原来 y 所在的儿子位置)指向 x,且 x 的父亲指向 z。fa[x]=z; if(z) ch[z][y==ch[z][1]]=x;
  • 实现

     void rotain(int x){
        int y = fa[x], z = fa[y], chk = get(x);
        ch[y][chk] = ch[x][chk ^ 1];
        if (ch[x][chk ^ 1]) fa[ch[x][chk ^ 1]] = y;
        ch[x][chk ^ 1] = y;
        fa[y] = x;
        fa[x] = z;
        if (z) ch[z][y == ch[z][1]] = x;
        maintain(y);
        maintain(x);
    }

    // maintain(x):在改变节点位置后,将节点x的size更新。
    void maintain(int x){
        sz[x] = sz[ch[x][0]] + sz[ch[x][1]] + cnt[x];
    }
    // 判断节点x是父亲节点的左儿子还是右儿子
    boolean get(int x){
        return x == ch[fa[x]][1];
    }

遍历

前提:二叉树非空

  • 前序遍历
    • 访问根节点
    • 遍历左子树
    • 遍历右子树
  • 中序遍历
    • 遍历左子树
    • 访问根节点
    • 遍历右子树
  • 后序遍历
    • 遍历左子树
    • 遍历右子树
    • 访问根节点
  • 层次遍历
    创建队列保存节点指针
    • 根节点
    • 根据节点层次,从左到右顺序对节点逐个访问

示例:
树遍历示例

遍历 结果
前序遍历 8 3 1 6 4 7 10 14 13
中序遍历 1 3 4 6 7 8 10 13 14
后续遍历 1 4 7 6 3 13 14 10 8
层次遍历 8 3 10 1 6 14 4 7 13

树的种类

  • 无序树:树中任意节点的子节点之间没有顺序关系,这种树称为无序树,也称为自由树。
  • 有序树:树中任意节点的子节点之间有顺序关系,这种树称为有序树;【大部分的树结构都是有序树】

二叉树

树的任意节点至多包含两棵子树。

  • 二叉树遍历:
    从二叉树的根节点出发,按照某种次序依次访问二叉树中的所有节点,使得每个节点被访问一次且仅被访问一次
  • 二叉树的访问次序:
    四种形式
    前序遍历;中序遍历;后序遍历;层次遍历

完全二叉树

对于一棵二叉树,假设其深度为d(d>1)。除了第d层外,其它各层的节点数目均已达最大值,且第d层所有节点从左向右连续地紧密排列,这样的二叉树被称为完全二叉树;
完全二叉树

满二叉树

所有叶节点都在最底层的完全二叉树。

满二叉树与完全二叉树最显著的区别是,满二叉树最底层叶子节点必须是填满。

满二叉树

哈夫曼树

又称为最优二叉树

  • 概念:
    带权路径最短的二叉树称为哈夫曼树。

树带权路径长度: 设二叉树具有 n 个带权叶结点,从根结点到各叶结点的路径长度与相应叶节点权值的乘积之和称为 树的带权路径长度(Weighted Path Length of Tree,WPL)。

$$
设w_i 为二叉树第 i 个叶结点的权值,l_i 为从根结点到第 i 个叶结点的路径长度,则 WPL 计算公式如下:
WPL=\sum_{i=1}^nw_il_i
$$
带权路径长度
计算过程:
WPL = 2×2+3×2+4×2+7×2 = 32
其中,WPL最小的二叉树就是哈夫曼树

  • 特点:
    对于霍夫曼树来说,其叶结点权值越小,离根越远,叶结点权值越大,离根越近,此外其仅有叶结点的度为 0,其他结点度均为 2。

哈夫曼算法

哈夫曼算法用于构造一棵哈夫曼树,算法步骤:

  1. 初始化: 由给定的n个权值构造n棵只有一个节点的二叉树,得到一个二叉树集合F
  2. 选取合并: 从二叉树集合F中选取节点权值最小的两棵二叉树分别作为左右子树构造一棵新的二叉树,这棵二叉树的根节点的权值为其左右子树根节点的权值和。
  3. 删除与加入: 从F中删除作为左、右子树的两棵二叉树,并将新建立的二叉树加入到F中
  4. 重复2、3步骤,直到F集合中只剩下一棵二叉树,这棵二叉树便是哈夫曼树

哈夫曼算法

哈夫曼编码

哈夫曼树可用于构造最短的前缀编码,即哈夫曼编码(Huffman Code),其构造步骤:

  1. 设需要编码的字符集为:$d_1,d_2,\dots,d_n$ ,他们在字符串中出现的频率为:$w_1,w_2,\dots,w_n$ 。
  2. 以 $d_1,d_2,\dots,d_n$ 作为叶结点,$w_1,w_2,\dots,w_n$ 作为叶结点的权值,构造一棵霍夫曼树。
  3. 规定哈夫曼编码树的左分支代表 0,右分支代表 1,则从根结点到每个叶结点所经过的路径组成的 0、1 序列即为该叶结点对应字符的编码。

举例说明:
{A,B,C,D,E}五个元素,出现的频率分别为{35,25,15,15,10},则
作图为:
哈夫曼树例题1
根据规定,0代表树左分支;1代表树右分支

元素 频率 编码
A 35 11
B 25 00
C 15 01
D 15 101
E 10 100

说明:
哈夫曼编码主要应用于通信,压缩。优先:空间效率高
等长编码:在进行二进制编码时,假设所有的代码都等长,那么表示 n 个不同的字符需要 $\left \lceil \log_2 n \right \rceil $位
程序设计时候,一般情况是不等长编码,为了高空间效率,则必须让频率高的字符采用尽可能短的编码,频率低的字符采用尽可能长的编码
在设计不等长编码时,要考虑解码的唯一性,如果一组编码中任一编码都不是其他任何一个编码的前缀,那么称这组编码为 前缀编码,其保证了编码被解码时的唯一性。

二叉查找树 & 平衡二叉树

  • 二叉查找树
    别名:二叉搜索树,BST
  • 特点:
  1. 若二叉查找树的左子树不为空,则其左子树上所有点的附加权值均小于其根节点的值;
  2. 若二叉查找树的右子树不为空,则其右子树上所有点的附加权值均大于其根节点的值;
  3. 二叉查找树的左右子树均为二叉查找树;
  4. 空树也是二叉查找树。

二叉查找树基本操作锁花费的时间与树的高度成正比。时间复杂度为(最优)$O(\log n)$,(最坏)$O(n)$

  • 平衡二叉树
    别名:AVL
    平衡二叉树是在二叉查找树的基础上,额外规定:每一个结点的左子树和右子树高度差最多为1

平衡二叉树适用于插入删除次数比较少,但查找比较多的情况

红黑树

红黑树是一种自平衡的二叉树。每个节点额外存储一个color字段("RED" or "BLACK"),用于确保树在插入和删除时保持平衡

  • 特点:
  1. 节点属性为 红色 或 黑色
  2. NIL节点(空叶子节点)为 黑色
  3. 红色节点的子节点为黑色
  4. 从根节点到 NIL节点的每条路径上的黑色节点数量相同

伸展树

Splay树,或 伸展树,是一种平衡二叉查找树,它通过 Splay/伸展操作 不断将某个节点旋转到根节点,使得整棵树仍然满足二叉查找树的性质,能够在均摊 O(\log N) 时间内完成插入,查找和删除操作,并且保持平衡而不至于退化为链。

  • 特点
    二叉树基础上,增加特性:左子树任意节点的值 < 根节点的值 < 右子树任意节点的值。

B-树

B 树(B-tree)是一种自平衡的树,能够保持数据有序。这种数据结构能够让查找数据、顺序访问、插入数据及删除的动作,都在对数时间内完成

  • 特点:
    B-树有两种节点:
    • 内部节点(internal node):存储了数据以及指向其子节点的指针。
    • 叶子节点(leaf node):与内部节点不同的是,叶子节点只存储数据,并没有子节点。
posted @ 2023-09-21 05:30  Bingo39  阅读(269)  评论(0)    收藏  举报