树的定义
结点的层次从根开始定义,根是第一层,根的孩子是第二层。树中结点的最大层次称为树的深度或高度。
如果将树中结点的各子树看成从左至右是有次序的,不可互换的,那该树就是有序树,否则为无序树。
森林是m(m>=0)棵互不相交的树的集合。
树的存储结构
双亲表示法
孩子表示法
由于树中每个结点都有可能有多棵子树,可以考虑用多重链表实现
方案一:根据树的度,声明足够的空间存放子树指针(浪费)
方案二:
虽然解决了空间浪费问题,但初始化和维护太麻烦
方案三:
对于孩子表示法,查找某个结点的某个孩子,或者找某个结点的兄弟,只需要查找这个结点的孩子单链表即可。但是当要寻找某个结点的双亲时,就不是那么方便了。所以可以将双亲表示法和孩子表示法结合,形成双亲孩子表示法。
方案四:
孩子兄弟表示法
任意一棵树,它的结点的第一个孩子如果存在就是唯一的,它的右兄弟存在也是唯一的。因此,设置两个指针,分别指向该结点的第一个孩子和此结点的右兄弟。
/* 树的孩子兄弟表示法结构定义*/
#define MAX_TREE_SIZE 100
typedef int ElemeType;
typedef struct CSNode{
ElemeType data;
struct CSNode * firstchild;
struct CSNode * rightsib;
}CSNode, *CSTree;
二叉树
特殊二叉树
斜树:
满二叉树:
在一棵二叉树中,如果所有分支节点都存在左子树和右子树,并且所有的叶子都在同一层上,这样的二叉树称为满二叉树。
特点:叶子只能在最下面那一层;非叶子节点的度一定是2;
在同样深度的二叉树中,满二叉树的结点个数一定最多,同时叶子也最多。
完全二叉树:
二叉树的性质
性质一:在二叉树的第i层上至多有2^(i-1)个结点,i >= 1
性质二:深度为k的二叉树至多有(2^k) - 1个结点,k >=1
性质三:
性质四:具有n个结点的完全二叉树的深度为 下限【log2n】+1
性质五:
二叉树的存储结构
二叉树是一种特殊的树,顺序存储和链式存储结构都能简单实现。二叉树的顺序存储结构就是用一维数组存储二叉树中的各个结点,并且结点的存储位置能体现结点之间的逻辑关系。
二叉树的遍历
前序遍历
若二叉树为空,则空操作返回,否则先访问根节点,然后前序遍历左子树,再前序遍历右子树。ABDH IEJC FKG
中序遍历
若树为空,则空操作返回,否则从根节点开始(不是先访问根节点),中序遍历根节点的左子树,然后访问根节点,最后中序遍历右子树。
后序遍历
若树为空, 则空操作返回,否则从左到右先叶子后结点的方式遍历访问左右子树,最后访问根节点。
层序遍历
ABCDEFG…
二叉树的建立和遍历代码
typedef char ElemType;
typedef strut BiTNode{
char data;
struts BiTNode *lchild, *rchild;
} BiTNode, *BiTree;
//创建一棵二叉树;按照前序遍历的方式输入数据
CreateBiTree(BiTree *T){
char c;
scanf("%c", c);
if('' == c){
*T = NULL;
} else {
*T = (BiTNode *)malloc(sizeof(BiTNode));
(*T) -> data = c;
CreateBiTree(&(*T) -> lchild);
CreateBiTree(&(*T) -> rchild);
}
}
// 访问二叉树结点的具体操作
visit(char c, int level){
printf("%c位于第%d层\n", c , level);
}
// 前序遍历
PreOrderTraverse(BiTree T, int level) {
if(T) {
visit(T->data, level);
PreOrderTraverse(T->lchild, level + 1);
PreOrderTraverse(T->rchild, level + 1);
}
}
int main(){
int level = 1;
BiTree T = NULL;
CreateBiTree(&T);
PreOrderTraverse(T, level);
return 0;
}
线索二叉树
利用二叉链表中的空指针域,存放指向结点在某种遍历次序下的前驱和后继结点的指针(这种附加的指针称为"线索")。
现将二叉树的结点结构重新定义如下:
#include<stdio.h>
#include<stdlib.h>
typedef char ElemType;
//线索存储标志位
//Link(0)表示指向左右孩子的指针
//Thread(1)表示指向前驱后继的线索
typedef enum {Link, Thread} PointerTag;
typedef struct BiThrNode{
char data;
struct BiThrNode *lchild,*rchild;
PointerTag ltag;
PointerTag rtag;
} BiThrNode, *BiThrTree;
// 全局变量,始终指向刚刚访问过得结点
BiThrTree pre;
//创建二叉树,约定用户遵照前序遍历的方式输入数据
CreateBiThrTree(BiThrTree *T){
char c;
scanf("%c", &c);
if('' == c){
*T = NULL;
} else {
*T = (BiThrNode *)malloc(sizeof(BiThrNode));
(*T) -> data = c;
(*T) -> ltag = Link;
(*T) -> rtag = Link;
CreateBiThrTree(&(*T) -> lchild);
CreateBiThrTree(&(*T) -> rchild);
}
}
// 中序遍历线索化
InThreading(BiThrTree T){
if(T){
InThreading(T -> lchild); //递归使左孩子线索化
if(!T -> lchild){
T -> ltag = Thread;
T -> lchild = pre;
}
if(!pre -> rchild){
pre -> rtag = Thread;
pre -> rchild = T;
}
pre = T;
InThreading(T -> rchild); //递归使you孩子线索化
}
}
InOrderThreading(BiThrTree *p, BiThrTree T){
*p = (BiThrTree)malloc(sizeof(BiThrNode));
(*p) -> ltag = Link;
(*p) -> rtag = Thread;
(*p) -> rchild = *p;
if(!T){
(*p) -> lchild = *p;
} esle {
(*p) -> lchild = T;
pre = *p;
InThreading(T);
pre->rchild = *p;
pre->rtag = Thread;
(*p)->rchild = pre;
}
}
int main(){
BiThrTree P, T = NULL;
CreateBiThrTree(&T);
InOrderThreading(&P, T);
return 0;
}
树,森林,二叉树的转换
树到二叉树的转换
森林到二叉树的转换
二叉树到树、森林的转换
判断一棵二叉树能够转换成一棵树还是森林,要看这个二叉树的根节点有没有右孩子,有就是森林,没有就是一棵树。
树与森林的遍历
树的遍历有两种方式,先根遍历和后根遍历。