(1). 树的定义:n节点的集合,集合中有一个称为根节点的特殊节点
① 在一棵树中,有且仅有一个节点没有前驱,这个节点就是树的根节点。
② 除根节点外,其余每个点有且仅有一个前驱
③ 每个节点可以有任意多个后续。
(2). 相关术语:
① 父节点、子节点、兄弟节点
② 节点的度、数的度
③ 叶节点和分支节点
④ 节点的层数
⑤ 树的深度
⑥ 有序树和无序树、森林
(1). 树中节点的最大度树没有限制而二叉树节点的最大度为2
(2). 树的节点无左右之分,而二叉树节点有左右之分
满二叉树、完全二叉树
(1). 满二叉树肯定是完全二叉树,而完全二叉树不一定是满二叉树。
(1)顺序存储:
① 顺序存储数据定义:
#define MAXSIZE 100
typedef int PADA;
typedef DATA SeqBinTinree[MAXSIZE];
SeqBinTree SBT;
② 如果顺序存储的二叉树不是完全二叉树,会比较麻烦。一般采取的方法是:将非完全二叉树转换为完全二叉树,将左侧缺点虚设为无数据的节点。(这种方法浪费空间。一般只适用于一些特殊情况)
(2). 链式存储:
一个节点可以由节点元素和两个分别指向左右子树的指针组成(二叉树链表结构)
二叉链式结构:
typedef structChainTree //定义二叉树结点类型
{
DATA data; //元素数据
struct ChainTree *left; //左子树结点指针
struct ChainTree *right; //右子树结点指针
}ChainBinTree;
a) 定义二叉树链式结构:
typedef structChainTree //定义二叉树结点类型
{
DATA data; //元素数据
struct ChainTree *left; //左子树结点指针
struct ChainTree *right; //右子树结点指针
}ChainBinTree;
b) 初始化二叉树:将已有的节点设置为二叉树的根节点。
c) 添加节点到二叉树:
d) 获取二叉树左右树
e) 获取二叉树状态:判断二叉树是否为空、求二叉树深度
f) 在二叉树中查找:在二叉树中查找数据时,需要遍历二叉树中的每个节点,逐个比较每一个节点的数据,然后然后该节点的指针。
g) 清空二叉树:
if(bt)
{
BinTreeClear(bt->left); //清空左子树
BinTreeClear(bt->right);//清空右子树
free(bt);//释放当前结点所占内存
bt=NULL;
}
(1). 先序遍历:先访问根节点,再按先序遍历左子树,最后按先序遍历右子树。
BinTree_DLR(BinTree){
If(二叉树不为空)
{
访问根节点;
BinTree_DLR(left);//先序遍历左子树
BinTree_DLR(right);//先序遍历右子树
}
}
(2). 中序遍历:先按中序遍历左子树,在访问根节点,最后访问右子树。
(3). 后序遍历:按后序遍历左子树,再按后续遍历右子树,最后访问根节点。
(1). 线索二叉树的表示:将每个节点中为空的做指针与右指针分别用于指针节点的前驱和后续,即可得到线索二叉树。
(2). 分类:先序线索二叉树,中序线索二叉树,后续线索二叉树
(3). 增加线索标志域后,二叉链表的结构如下:
typedef enum
{
SubTree,
Thread
}NodeFlag; //枚举值SubTree(子树)和Thread(线索)分别为0,1
typedef struct ThreadTree //定义线索二叉树结点类型
{
DATA data; //元素数据
NodeFlag lflag; //左标志
NodeFlag rflag; //右标志
struct ThreadTree *left; //左子树结点指针
struct ThreadTree *right; //右子树结点指针
}ThreadBinTree;
(4). 对二叉树进行中序线索化。
(5).操作线索二叉树:
a). 查找后续节点:
i. 若父节点P的右子树为空,则p->right为右线索,直接指向p的中序后续。
ii. 若父节点p的右子树不为空,则p的中序后续必是其右子树中第一个中序遍历到的节点。
b). 查找前驱节点:
i. 若父节点p的左子树为空,则p->left为左线索,直接指向p的中序前驱节点
ii. 若父节点p的左子树不为空,则从p的左子树出发,沿该子树的右指针链表往下查找,直到一个没有右子树的节点为止,则该及诶按就是p的中序前驱节点。
c). 遍历线索二叉树:由于二叉树被线索化,遍历时可根据后续指针快速完成,而不使用递归调用即可完成。
(1). 指的是对于一组具有确定权值的叶子树节点的具有最小带权路径长度的二叉树
(2). 基本概念:节点的权,路径,路径的长度,树的路径长度,节点的带权路径长度,树的带权路径长度(WPL)
(3).构造哈弗曼树的步骤:
a). 首先根据N个权值的节点构造N棵二叉树,每个节点为一个二叉树,其权值保持在根节点中,其中左右子树均为空。
b). 在这N棵二叉树中查找出两颗根节点权值最小的树,用着两颗树作为左右子树构造一颗新的二叉树,将左右子树根节点的权值相加,作为新二叉树根节点的权值。
c). 将上步找到的两颗权值最小的二叉树排除在下次查找查找之外,将新创建的二叉树添加到查找范围之内。
d) . 重复步骤b与c,直到查找范围只剩下一棵树为止。
(4). 哈夫曼编码:首先在内存分配一片连续的区域,用来保存哈夫曼二叉树,可将这部分内存作为一个以为数组,然后通过数组的下标访问不同的二叉树节点。
(5). 哈夫曼编码的一些操作:
a) . 创建哈弗曼树
b). 根据哈弗曼树申城每一个字符的哈弗曼树编码
c). 根据该哈夫曼编码将字符串进行编码,得到编码后的字符串
d). 从子字符串中逐个获取二进制编码,并不断在哈弗曼树中进行查找,完成解码工作。
元素之间的关系是任意的,每个数据元素都可以和其他数据元素相关
(1). 邻接矩阵:(用两个数组)
a). 第一个数组:具有n个元素的一维数组,用来保存图中个顶点的信息。
b). 第二个数组:n*n的二维数组(矩阵),用来保存顶点之间相邻的关系。
(2). 邻接表:是由邻接矩阵改进而来的一种连接结构,其特点值考虑邻接矩阵中的非零元素,因而比邻接矩阵保存图要节省空间。邻接表对图中的每个顶点建立一个邻接关系的单链表,邻接矩阵中的每一行对应一个单链表,将每个单链表的表头指针保存到一个数组中一方便随机访问任意一个顶点链表。
(1). 用邻接矩阵:
a). 创建邻接矩阵数据结构
b). 创建邻接矩阵
c). 输出邻接矩阵的内容。
(2).用邻接表保存图
a). 定义邻接表数据结构(定义顶点的数据结构)
b). 定义图的数据结构
c). 创建邻接图
d). 输出邻接图
广度优先法和深度优先法
(1). 广度优先遍历:广度优先遍历不是递归的过程。在遍历的过程中,先被访问的顶点,其邻接点也先被访问,算法的实现需要设置一个队列,用来一次记住被访问过的顶点。
(2). 深度优先遍历:深度优先遍历是一个递归的过程
a). 从isTrav数组中选择一个未被访问的顶点Vi,将其标记为已访问;
b). 接着从Vi中的一个未被访问的邻接点出发进行深度优先遍历;
c). 重复步骤b,直至图中所有和Vi有路径相通的顶点都被访问过;
d). 重复a到c的步骤操作,直至所有的顶点都被访问过。
对于有N个顶点的连通图,生成树有N-1条边。对于一个带权值的连通图,生成树不同,树中各边的权值总和也不同,权值最小的生成树称为图的最小生成树称为图的最小树。
(1). 构造生成树的常用算法prim:要实现prim算法,需要连个辅助数组
a). 数组1:tmpvertex在对应位置保存邻接顶点信息。
b). 数组2:weight保存与上一个数组对应邻接边的权值。
求一个顶点到另一个顶点的最短路径。
实现的过程需要三个数组来辅助实现:(集合U是一个顶点集合,把求到的最短路径顶点添加到集合U中)
第一个:用来保存顶点集合,diagram权值为1时,表示对应的顶点被添加到U集合中
第二个:保存临时计算出来权值
第三个:表示是否存在对应的边。
实现的代码如下:
void Dijkstra(MatrixGraph G)
{
int weight[VERTEX_MAX];//某源点到各顶点的最短路径长度
int path[VERTEX_MAX];//某源点到终点经过的顶点集合的数组
int tmpvertex[VERTEX_MAX];//最短路径的终点集合
int i,j,k,v0,min;
printf("\n请输入源点的编号:");
scanf("%d",&v0);
v0--; //编号自减1(因数组是从0开始)
for(i=0;i0) //有效权值
path[i]=v0; //保存边
tmpvertex[i]=0; //初始化顶点集合为空
}
tmpvertex[v0]=1; //将顶点v0添加到集合U中
weight[v0]=0; //将源顶点的权值设为0
for(i=0;i