一.树的基本概念和性质
树的基本概念
节点(Node):
树中的每个元素称为一个节点,每个节点包含一个值(或称为数据)和若干指向其子节点的引用。
根节点(Root):
树的最顶层的节点,没有父节点。
子节点(Child):
直接连接到另一个节点的节点。
父节点(Parent):
有一个或多个直接子节点的节点。
叶节点(Leaf)/终端节点(Terminal Node):
没有子节点的节点。
子树(Subtree):
任何一个节点及其所有后代构成的树。
兄弟节点(Sibling):
拥有相同父节点的节点。
边(Edge):
连接两个节点的线段,表示它们之间的父子关系。
路径(Path):
从一个节点到另一个节点经过的节点序列。
祖先(Ancestor):
从根节点到某个节点的路径上的所有节点。
后代(Descendant):
某个节点的所有子节点、子节点的子节点等。
森林(Forest):
由多棵树构成的集合。
树的性质
层次性(Hierarchical):
树是一种层次结构,每个节点都有一个父节点(除了根节点),可以有零个或多个子节点。
递归性(Recursive):
树的定义是递归的,即一棵树是由多棵子树构成的。
深度(Depth):
从根节点到某个节点的路径上的边数称为该节点的深度。
高度(Height):
从某个节点到最远叶节点的最长路径上的边数称为该节点的高度。树的高度是从根节点到最远叶节点的路径长度。
有序性(Ordering):
在某些特定类型的树(如二叉搜索树)中,节点的子树之间存在顺序关系。
节点数与边数的关系:
在任何树中,节点数比边数多1。即如果树有n个节点,那么它有n-1条边。
树的常考性质
- 常见考点1: 结点数 = 总度数 + 1
树的度——各结点的度的最大值 m叉树——每个结点最多只能有m个孩子的树
度为m的树 m叉树 任意结点的度 ≤ m (最多m个孩子) 任意结点的度 ≤ m (最多m个孩子) 至少有一个结点度 = m (有m个孩子) 允许所有结点的度都<m 一定是非空树,至少有m+1个结点 可以是空树
- 常见考点2: 度为m的树, m叉树的区别
- 常见考点3: 度为m的树第i层至多有 m^i-1 个结点( i ≥ 1 )
- m叉树第i层至多有 m^i-1 个结点( i ≥ 1 )
- 常见考点4: 高度为h的m叉树至多有mʰ-1 / m -1 个结点
- 常见考点5: 高度为h的m叉树至少有h个结点。
- 高度为h、度为m的树至少有h+m+1个结点
- 常见考点6: 具有n个结点的m叉树的最小高度为[logₘ(n(m-1)+1)]
二.二叉树
基本概念
- 节点(Node):二叉树中的每个元素称为一个节点,每个节点包含数据部分和两个指向其子节点的引用(通常称为左子节点和右子节点)。
- 根节点(Root):二叉树的最顶层节点,没有父节点。
- 叶节点(Leaf):没有子节点的节点。
- 子树(Subtree):任何节点及其后代构成的树。
- 深度(Depth):从根节点到某个节点的路径长度(边的数量)。
- 高度(Height):从某个节点到最远叶节点的最长路径长度(边的数量)。
是n个结点的有限集合 可为空二叉树 也可由一个根结点和两个互不相交的左子树,右子树组成
特点: 1.每个结点至多只能有两棵子树 2.左右子树不能颠倒(二叉树是有序树)
二叉树的五种状态: 空二叉树 只有左子树 只有右子树 只有根节点 左右子树皆有
二叉树的常考性质
二叉树
- 常见考点1:非空二叉树中度为0,1,2的结点数分别为n0,n1,n2。则n0=n2+1(叶子结点比二分支结点多一个)
- 常见考点2:二叉树第i层至多有2^i-1个结点
- m叉树第i层至多有m^i-1个结点
- 常见考点3:高度为h的二叉树至多有 2ʰ - 1 (满二叉树)
(树的常考性质4) 高度为h的m叉树至多有mʰ-1 / m -1 个结点 (m=2得上)
完全二叉树
- 常见考点1:具有n个(n>0)结点的完全二叉树的高度h为[[log₂(n+1)] 或 [[log₂n]+1
几种特殊的二叉树
满二叉树
- 不存在度为1的结点
- 只有最后一层有叶子结点
- 结点i的左孩子为2i,右孩子为2i+1,父节点为[i / 2] (以下i均从1开始)
完全二叉树
- 除了最后一层外,每一层都被完全填满,且所有节点都尽可能地向左。
- 最多只有一个度为1的结点
- i ≤ [n/2]为分支结点 ,i ≥ [n/2]为叶子结点
二叉排序树
- 左子树上的所有结点的关键字均小于根结点的关键字
- 右子树上的所有结点的关键字均大于根结点的关键字
- 左右子树又各是一颗二叉排序树
平衡二叉树
- 树上任意结点的左子树,右子树的高度/深度差都不超过 1。
- AVL 树和红黑树是最常见的平衡二叉树。
二叉树的存储结构
- 顺序存储
一定要把二叉树的结点编号与完全二叉树对应起来
最坏的情况:高度为h且只有h个结点的单支树(所有结点只有右孩子),也至少需要2ʰ-1个存储 单元
#define MaxSize 100 struct TreeNode { int data; bool isEmpty; }; void InitTreeNode(TreeNode* t) { for (int i = 0; i < MaxSize; i++) { t->isEmpty = true; } } //几个重要的基本操作 //i的左孩子 2i //i的右孩子 2i+1 //i的父节点 [i/2] //i所在的层次 [log2(n+1)]或[log2n]+1 int main() { TreeNode t[MaxSize]; InitTreeNode(t); return 0; }
- 链式存储
- n个结点的二叉链表共有n+1个空链域
struct ElemType { int value; }; typedef struct BinTree { ElemType data; //数据域 struct BinTree* lchild;//左指针 struct BinTree* rchild;//右指针 }BiTNode,*BiTree; int main() { //定义一棵空树 BiTree root = NULL; //插入根节点 root = (BiTree)malloc(sizeof(BiTree)); root->data = { 1 }; root->lchild = NULL; root->rchild = NULL; //插入新结点 BiTNode *p= (BiTNode*)malloc(sizeof(BiTNode)); p->data = { 2 }; p->lchild = NULL; p->rchild = NULL; root->lchild = p; return 0; }
二叉树的遍历
先序
根左右
嵌套使用 根 (根左右/左子树) (根左右/右子树)
根 [ 根 (根左右/左子树) (根左右/右子树)) (根 (根左右/左子树) (根左右/右子树)) ...
struct ElemType { int value; }; typedef struct BinTree { ElemType data; //数据域 struct BinTree* lchild;//左指针 struct BinTree* rchild;//右指针 }BiTNode,*BiTree; void visit(BiTree t) { } void PreOrder(BiTree T) { if (T != NULL) { visit(T); PreOrder(T->lchild); PreOrder(T->rchild); } }
中序
左根右
嵌套使用 (左根右/左子树) 根 (左根右/右子树)
((左根右/左子树) 根 (左根右/右子树)) 根 ((左根右/左子树) 根 (左根右/右子树)) ...
void InOrder(BiTree T) { if (T != NULL) { PreOrder(T->lchild); visit(T); PreOrder(T->rchild); } }
后序
左右根
嵌套使用 (左根右/左子树)(左根右/右子树)根
((左根右/左子树)(左根右/右子树)根)((左根右/左子树)(左根右/右子树)根)根...
void PostOrder(BiTree T) { if (T != NULL) { PreOrder(T->lchild); PreOrder(T->rchild); visit(T); } }
层次遍历
算法思想:
- 初始化一个辅助队列
- 根结点入队
- 若队列非空,则队头结点出队,访问该结点并将其左右孩子插入队尾
- 重复3直至队列为空
void LevelOrder(BiTree T) { LinkQueue Q; InitQueue(Q); BiTree p; EnQueue(Q, T); while (!isEmpty(Q)) { DeQueue(Q, p); visit(p); if (p->lchild != NULL) { EnQueue(Q, p->lchild); } if (p->rchild != NULL) { EnQueue(Q, p->rchild); } } }