🌞 “永远积极向上,永远豪情满怀,永远热泪盈眶!”
哈夫曼树与图
- 🎈1.哈夫曼树
- 🔭1.1树与二叉树的转换
- 🔭1.2森林与二叉树的转换
- 🔭1.3哈夫曼树
- 🔎1.3.1哈夫曼树的概念
- 🔎1.3.2哈夫曼树的构造
- 🔎1.3.3例题
- 🎈2.图
- 🔭2.1图的定义
- 🔭2.2图的基本术语
- 🔭2.3图的抽象数据类型
- 🔭2.4图的存储结构
- 📖2.4.1邻接矩阵存储
- ✅2.4.1.1邻接矩阵表示的类定义
- ✅2.4.1.2创建图
- ✅2.4.1.3定位操作-查找顶点信息在顶点数组中的下标
- ✅2.4.1.4创建有向图或无向图
- ✅2.4.1.5创建有向网或无向网
- ✅2.4.1.6计算顶点的度数-有向图为例
- ✅2.4.1.7以有向图为例
🎈1.哈夫曼树
🔭1.1树与二叉树的转换
由于二叉树和树均可以用二叉链表作为存储结构,因此以二叉链表为媒介可以倒数树和二叉树的对应关系。一棵树可以找到唯一的一棵二叉树与之对应。反之,一棵右子树为空的二叉树也可以转换成对应的树。
📝将一棵树转化为二叉树的方法如下:
- 加线:将树中所有相邻的兄弟之间加一条线。
- 抹线:保留树中每个结点与其第一个孩子结点之间的连线,删除它与其他孩子结点之间的连线。
- 旋转:以右侧有结点连线的结点为轴心,将其右侧结点顺时针旋转45°,使之成为一棵层次分明的二叉树。
🔭1.2森林与二叉树的转换
从树和二叉树的转换方式知,任何一棵树转换成二叉树,二叉树的右子树必为空。由此,我们可以把森林中的第二棵树的根结点看作第一棵树的根结点的兄弟,第三棵树的根结点看成是第二棵树的根结点的兄弟。
🔭1.3哈夫曼树
🔎1.3.1哈夫曼树的概念
哈夫曼树,又称最优二叉树,是一类树的带权路径长度最小的二叉树。
- 路径:从一个祖先结点到子孙结点之间的分支构成这两个结点间的路径。
- 路径长度:路径上分支的数目。
- 树的路径长度:从根到每个结点的路径长度之和。
- 结点的权:根据应用的需要可以给树的结点赋予权值。
- 结点的带权路径长度:从根到该结点的路径长度与该结点权的乘法。
- 树的带权路径长度:树中所有叶子结点的带权路径长度之和。
带权路径长度最小的二叉树称为哈夫曼树,也称最优二叉树。
🔎1.3.2哈夫曼树的构造
🔎1.3.3例题
🧩定理:对于具有n个叶子结点的哈夫曼树,共有2n-1
个结点,其中有n-1
个非叶子结点。
🎈2.图
🔭2.1图的定义
称有序二元组
G=(V,E)
是一个图,若其中V
是一个非空有限集合(或称顶点集),V
中的数据元素称为顶点,E
是V
上的二元关系,称E
为边集。
(1).若E
中的顶点对(或序偶)(u,v)
是无序的,则称G
为无向图,称(u,v)
为G
的一条无向边,显然,(u,v)
和(v,u)
是指同一条边。
(2).若E
中的顶点对(或序偶)(u,v)
是有序的,则称G
为有向图。称(u,v)
为G
的一条有向边(或弧),其中,称u
为弧尾(或始点),称v
为弧头(或终点)。
🔭2.2图的基本术语
- 端点、邻接点、自环和孤立点:在一个无向图
G=(V,E)
中,若e=(u,v)
是G
中的一条边,u
和v
称为边e
的两个端点,称边e
关联u
和v,
也称u
邻接v
,或v
邻接u
,u
和v
称为边e=(u,v)
的邻接点;若u=v
,称(u,v)
为G
的自环。对于任意的u
属于V
,若不存在任何边关联u
,说明顶点u
是孤立点。 - 完全图:令
G=(V,E)
为一个图,且|V|=n,|E|=m
。若无向图中每两个顶点间都存在一条边,或在有向图中每两个顶点间都存在着方向相反的两条有向边,则称此图为完全图。显然,无向图包含n(n-1)/2
条边,有向图包含n(n-1)
条边。 - 稀疏图和稠密图:边数很少的图
(m<nlogn)
称为稀疏图,反之称为稠密图。 - 顶点的度、出度和入度:在无向图
G=(V,E)
中,对于每一个v
属于V
,称关联顶点v
的边数为顶点v
的度数,记为d(v).
在有向图G=(V,E)中,对于每一个v属于V,称以v为始点的边的条数为v的出度,称以v为终点的边的条数为v的入度。在无向图中,顶点的度数之和与边数有如下关系:
在有向图中,顶点的度数之和与边数有如下关系:
-
子图、真子图和生成子图:G=(V,E),H=(V1,E1)是两个图。
-
路径、路径长度、简单路径和初等路径:
-
回路、简单回路和初等回路:
-
连通、连通图和连通分量:
-
连通、强连通和强连通分量:
-
权和网:在某些图中,边或弧上具有与它相关的数据信息称之为权。在实际应用中,权值可以有某种含义。例如,在城市交通线路的图中,边上的权值可以来表示该线路上的长度或交通费用等。边上带权的图称为带权图,也称网。
-
生成树:包含
G
的n
个顶点的极小连通子图称为图G
的生成树。一棵具有n
个顶点的生成树有且仅有n-1
条边。
🔎例题:图
G=(V,E)
是一个非连通无向图,共有36
条边,则该图至少有多少个顶点?
该非连通无向图至少由一个顶点和一个连通无向图组成。
(n-1)n/2=36,n=9
,所以该图至少有10
个顶点。
🔭2.3图的抽象数据类型
ADT Graph{
数据对象:
D = V = {ai|1<=i<=n,ai属于Elemtype}
数据关系:
R = E = {(ai,aj)|1<=i<=n,1<=j<=n,ai,aj属于D}
基本操作:
InitGraph(&g,V,E);//初始化创建一个图
DestroyGraph(&g);//摧毁图,释放图占用的存储空间
LocateVex(g,v);//查找顶点v在图中的位置
InsertVex(&g,v);//在图中插入一个顶点v
DeleteVex(&g,v);//在图中删除一个顶点v
Degree(g,v);//计算顶点v的度数
DFETraverse(g,v);//从顶点v出发对图进行深度优先遍历
BFSTraverse(g,v);//从顶点v出发对图进行广度优先遍历
}ADT Graph
🔭2.4图的存储结构
📖2.4.1邻接矩阵存储
🔎图的邻接矩阵存储结构具有如下特点:
- 无向图的邻接矩阵是对称矩阵,实际存储时可以采用压缩矩阵存储。
- 有向图的邻接矩阵不一定对阵,因此,采用邻接矩阵存储具有n个顶点的有向图时,其存储结构的空复杂度为O(n2)。
- 无向图邻接矩阵的第i行(或第i列)中非0元素的个数,就是顶点i的度数。
- 有向图邻接矩阵的第i行中非0元素的个数,就是顶点i的出度,第i列中非0元素的个数,就是顶点i的入度。
- 利用邻接矩阵可以较容易地确定图中两顶点之间是否有边。但若要确实图的总边数,则要按行按列进行查询,耗费的时间代价较大。另外,邻接矩阵用数组表示时,静态数组的局限性不便于图的插入和删除操作,它们是邻接矩阵存储结构的局限性。
✅2.4.1.1邻接矩阵表示的类定义
#define _CRT_SECURE_NO_WARNINGS 1
#define MaxVex 20//预设最大顶点数
const int MAXINT = 0xfffffff;//最大整数,表示无穷大
typedef struct
{
VRType adj;//VRType为顶点关系类型,对无权图值为0或1
InfoType info;//该弧相关信息
}ArcBox;
typedef struct
{
VElemType vexs[MaxVex];//VElemType为顶点信息类型
ArcBox arcs[MaxVex][MaxVex];//邻接矩阵,即边表
int vexnum;//顶点数
int arcnum;//边数
int kind;//邻接矩阵的存储的图的种类:1有向图,2无向图,3有向网,4无向网
}MGraph;
class Graph
{
private:
MGraph mg;
public:
void CreateGraph();//创建图
int LocateVex(VElemType x);//返回图中顶点x在顶点数组中的下标
void CreateDG();//构造有向图
void CreateUDG();//构造无向图
void CreateDN();//构造有向网
void CreateUDN();//构造无向网
int DegreeGraph(VElemType x);//计算顶点的度数
void ShortPath_DIJ(VElemType v);//求最短路径
void Dispath(int dist[], int path[], int s[], VElemType x);//输出最短路径
void Floyd();//Floyd算法求最短路径
void Dispath(int dist[MaxVex][MaxVex]);//输出最短路径
};
✅2.4.1.2创建图
void Graph::CreateGraph()
{
cout << "输入图的种类:1:有向图 2:无向图 3:有向网 4:无向网";
cin >> mg.kind;
switch (mg.kind)
{
case 1:CreateDG();//构造有向图
break;
case 2:CreateUDG();//构造无向图
break;
case 3:CreateDN();//构造有向网
break;
case 4:CreateUDN();//构造无向网
}
}
✅2.4.1.3定位操作-查找顶点信息在顶点数组中的下标
int Graph::LocateVex(VElemType x)
{
for (int i = 0; i < mg.vexnum; i++)
{
if (x == mg.vexs[i])
return i;
return -1;
}
}
✅2.4.1.4创建有向图或无向图
void Graph::CreateDG()
{
int i, j, h, t;
VElemType u, v;
int flag;//0表示没有弧信息,1表示有弧信息
cin >> mg.vexnum >> mg.arcnum >> flag;
for (int i = 0; i < mg.vexnum; i++)
{
cin >> mg.vexs[i];//构造顶点信息
}
for (i = 0; i < mg.vexnum; i++)
{
for (j = 0; j < mg.vexnum; j++)
{
mg.arcs[i][j].adj = 0;//用0初始化邻接矩阵
mg.arcs[i][j].info = NULL;//用NULL初始化该弧的相关信息
}
}
for (i = 0; i < mg.arcnum; i++)
{
cin >> u >> v;
h = LocateVex(u);
t = LocateVex(v);
if (flag)
{
cin >> mg.arcs[h][t].info;
}
mg.arcs[h][t].adj = 1;//1表示顶点相邻
mg.arcs[t][h].adj = 1;//若为无向图加上这条
}
}
✅2.4.1.5创建有向网或无向网
void Graph::CreateDN()
{
int i, j, h, t, w;
VElemType u, v;
int flag;//0表示没有弧信息,1表示有弧信息
cin >> mg.vexnum >> mg.arcnum >> flag;
for (int i = 0; i < mg.vexnum; i++)
{
cin >> mg.vexs[i];//构造顶点信息
}
for (i = 0; i < mg.vexnum; i++)
{
for (j = 0; j < mg.vexnum; j++)
{
if (i == j)
{
mg.arcs[i][j].adj = 0;//用0初始化邻接矩阵
}
else
{
mg.arcs[i][j].adj = MAXINT;
}
mg.arcs[i][j].info = NULL;//用NULL初始化该弧的相关信息
}
}
for (i = 0; i < mg.arcnum; i++)
{
cin >> u >> v >> w;
h = LocateVex(u);
t = LocateVex(v);
if (flag)
{
cin >> mg.arcs[h][t].info;
}
mg.arcs[h][t].adj = w;
mg.arcs[t][h].adj = w;//若为无向网加上该赋值
}
}
✅2.4.1.6计算顶点的度数-有向图为例
int Graph::DegreeGraph(VElemType x)
{
int h = LocateVex(x);
int count = 0;
for (int i = 0; i < mg.vexnum; i++)
{
if (mg.arcs[h][i].adj && mg.arcs[h][i].adj != MAXINT)
count++;
if (mg.arcs[i][h].adj && mg.arcs[i][h].adj != MAXINT)
count++;
}
return count;
}
✅2.4.1.7以有向图为例
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
typedef int VElemType;
typedef int VRType;
typedef int InfoType;
#define MaxVex 20//预设最大顶点数
const int MAXINT = 0xfffffff;//最大 整数,表示无穷大
typedef struct
{
VRType adj;//VRType为顶点关系类型,对无权图值为0或1
InfoType info;//该弧相关信息
}ArcBox;
typedef struct
{
VElemType vexs[MaxVex];//VElemType为顶点信息类型
ArcBox arcs[MaxVex][MaxVex];//邻接矩阵,即边表
int vexnum;//顶点数
int arcnum;//边数
int kind;//邻接矩阵的存储的图的种类:1有向图,2无向图,3有向网,4无向网
}MGraph;
class Graph
{
private:
MGraph mg;
public:
void CreateGraph();//创建图
int LocateVex(VElemType x);//返回图中顶点x在顶点数组中的下标
void CreateDG();//构造有向图
void CreateUDG();//构造无向图
void CreateDN();//构造有向网
void CreateUDN();//构造无向网
int DegreeGraph(VElemType x);//计算有向图顶点的度数
};
void Graph::CreateGraph()
{
cout << "输入图的种类:1:有向图 2:无向图 3:有向网 4:无向网"<<endl;
cout << "请输入:";
cin >> mg.kind;
switch (mg.kind)
{
case 1:CreateDG();//构造有向图
break;
case 2:CreateUDG();//构造无向图
break;
case 3:CreateDN();//构造有向网
break;
case 4:CreateUDN();//构造无向网
}
}
int Graph::LocateVex(VElemType x)
{
for (int i = 0; i < mg.vexnum; i++)
{
if (x == mg.vexs[i])
return i;
}
return -1;
}
void Graph::CreateDG()
{
int i, j, h, t;
VElemType u, v;
int flag;//0表示没有弧信息,1表示有弧信息
cout << "请输入顶点数、边数以及是否有弧信息(0/1)";
cin >> mg.vexnum >> mg.arcnum >> flag;
cout << "请输入" << mg.vexnum << "个顶点:";
for (int i = 0; i < mg.vexnum; i++)
{
cin >> mg.vexs[i];//构造顶点信息
}
for (i = 0; i < mg.vexnum; i++)
{
for (j = 0; j < mg.vexnum; j++)
{
mg.arcs[i][j].adj = 0;//用0初始化邻接矩阵
mg.arcs[i][j].info = NULL;//用NULL初始化该弧的相关信息
}
}
for (i = 0; i < mg.arcnum; i++)
{
cout << "请输入关联的两个顶点:";
cin >> u >> v;
h = LocateVex(u);
t = LocateVex(v);
if (flag)
{
cout << "请输入权值:";
cin >> mg.arcs[h][t].info;
}
mg.arcs[h][t].adj = 1;//1表示顶点相邻
}
}
int Graph::DegreeGraph(VElemType x)
{
int h = LocateVex(x);
int count = 0;
for (int i = 0; i < mg.vexnum; i++)
{
if (mg.arcs[h][i].adj && mg.arcs[h][i].adj != MAXINT)
count++;
if (mg.arcs[i][h].adj && mg.arcs[i][h].adj != MAXINT)
count++;
}
cout << "该顶点的度数为:" << count << endl;
return 0;
}
int main()
{
Graph a;
a.CreateGraph();
a.DegreeGraph(1);
a.DegreeGraph(2);
a.DegreeGraph(3);
a.DegreeGraph(4);
return 0;
}
✅运行示例: