04 树
树
- 有且仅有一个特定的根节点,根节点只有直接后继,没有直接前驱
树的存储结构
- 双亲表示法
- 双亲域:保存结点的双亲所在位置
- 数据域
#define MAX_TREE_SIZE 100; //结点结构 typedef struct PTNode{ TElemType data; int parent; }PTNode; //树结构 typedef struct{ PTNode nodes[MAX_TREE_SIZE]; int r,n;//根的位置和结点数 }PTree;
- 孩子表示法
- 每个结点有多个指针域,分别指向子树的根
typedef struct CTNode { // 孩子结点 int child; struct CTNode *next; } *ChildPtr; typedef struct { ElemType data; // 结点的数据元素 ChildPtr firstchild; // 孩子链表头指针 } CTBox; typedef struct { CTBox nodes[MAX_TREE_SIZE]; int n, r; // 结点数和根结点的位置 } CTree;
- 带双亲的孩子链表
- 以上要不是找不到子节点,就是找不到父亲结点,把它们结合起来就可以解决啦
- 孩子兄弟表示法
- 用二叉链表做树的存储结构,链表中每个结点的两个指针域分别指向第一个孩子结点和下一个兄弟结点
- 特点:操作容易但破坏了树的层次结构
typedef struct CSNode{ ElemType data; struct CSNode *firstchild,*nextsibling; } CSNode, *CSTree;
树的遍历
- 方法
- 先根后子树
- 先子树后根
- 按层次遍历(广度优先)
森林与树的转换
森林转换为二叉树
- 各树先分别转换为二叉树
- 将每个树的根节点连线
- 以第一个树的根节点为二叉树的根,再以根为轴心顺时针旋转形成二叉树
二叉树转换为森林
- 将二叉树每个结点的根节点与其右孩子的连线抹除
树转换为二叉树
- 兄弟间加线
- 对每个结点,除了左结点,取出其与其余孩子之间的关系
- 整个树顺时针旋转45度
二叉树转换为树
- p是左孩子结点,则将p的右孩子,右孩子的右孩子…分别与双亲连线
- 抹掉双亲与右孩子之间的连线形成树结构
二叉树
- 每个结点至多有两棵子树
- 性质
- 第i层至多有2i-1个结点
- 深度为看的二叉树至多有2k-1个结点
- 任何一个二叉树T,如果叶节点数为 n0,度为2的结点数为n2,则n0=n2+1
- 具有n个结点的完全二叉树的深度floor(log(n))+1
- 如果将一个有n个结点的完全二叉树自顶向下,同层自左向右连续为结点编号
- i=0,无双亲;i>0,双亲为floor((i-1)/2)
- 顺序存储时很重要:若2*i+1<n,则左子女为2*i+1<n
- 顺序存储时很重要:若2*i+2<n,则左子女为2*i+2<n
- 结点编号为偶数且i!=0,则左兄弟结点为i-1
- 结点编号为奇数且i!=n-1,则右兄弟结点为i+1
- 结点i所在层次为floor(log(i+1))
- 存储结构
- 顺序存储
- 链式存储
- 二叉链表
- 左结点lchild
- 数据data
- 右结点rchild
- 二叉链表
//二叉树数据结构定义 typedef char Treedata; //结点数据类型 typedef struct node{ TreeData data; struct node *leftChild,*rightChild; }BinTreeNode; typedef BinTreeNode *BinTree; //根节点
- 三叉链表
- 数据data
- 父节点parent
- 左结点lchild
- 右结点rchild
- 二叉树遍历
- 访问节点为v,左子树为l,右子树为r,以v的位置判断是哪种遍历
- 前序遍历vlr
- 中序遍历lvr
- 后序遍历lrv
满二叉树
- 最后一层都是叶节点,其他层结点的度都为2
完全二叉树
- 除了最后一层,其他层的结点数都达到最慢