第七章 (接知识点总结2) 图
图的遍历:
//深度优先搜索
#define OK 1
#define True 1
#define Error -1
#define False 0
typedef enum{DG, DN, UDG. UDN}Graph;
int visited[MAX];
//Graph代表图的一种存储结构比如邻接表,邻接矩阵
void TranverseGraph(Graph g){
int vi;
for(vi = 0; vi < g.vernum; vi++)
visited[vi] = False;
for(vi = 0; vi < g.vernum; vi++){//连通图那么此操作执行一次
if(!visited[vi])
DepthFirstSearch(g, vi);
}
}
void DepthFirstSearch(Graph g, int v0)
{
visit(v0);
visited[v0] = True;
w = FirstAdjVertex(g, v0);
while(w != -1){
if(visited[w])
DepthFirstSearch(g, w);
w = NextAdjVertex(g, v0, w);
}
}
深度遍历总结:总之就是一直找邻接点,找到一个那么找这个结点的下一个邻接点,找不到就换...
对于邻接表:
void DepthFirstSearch(AdjList g, int v0){
visit(v0);
visited[v0] = True;//这里的True是宏定义,bool类型的为true
ArcNode *p;
p = g.vertex[v0].firstarc;
while(p){
if(!visited[p->adjvex])
DepthFirstSearch(g, p->adjvex);
p = p->nextarc;
}
}
图的遍历--广度优先搜索
void BreadFirstSearch(Graph g int v0){
visit(v0);
visited[v0] = True;//标志数组,证明该结点已经访问过
InitQueue(&Q);
EnterQueue(&Q, v0);
while(!IsEmpty(Q)){
DeleteQueue(&Q, &v);
w = FirstAdjVertex(g, v);
while(w != -1){
if(!visited[w]){
visit(w);
visited[w] = True;
EnterQueue(&Q, w);
}
w = NextAdjVertex(g, v, w);
}
}
}
广度遍历总结:就是一层一层的访问,访问完这一个结点衍生出去路径为1的结点,在走下一层...
图的应用
//求图中两个节点的简单路径
int pre[];
void one_path(Graph *G, int u, int v)
{
//找到一条从u到v结点的简单路径
int i;
pre = (int *)malloc(G->vexnum * sizeof(int));
for(i = 0; i < G->vernum; i++)
pre[i] = -1;//未访问标志
pre[u] = -2;//访问了,无前驱
DSF_path(G, u, v);
free(pre);
}
int DSF_path(Graph *G, int u, int v){
int j;
for(j = FirstAdj(G, u); j >= 0; j = nextadj(G, u, j)){
if(pre[j] == -1){
pre[j] = u;
if(j == v)
{
print_path(pre, v);
return 1;
}
else if(DFS_path(G, j, v))
return 1;
}
}
return 0;
}
生成树:一个连通图的生成树是一个极小联通子图, 含有全部顶点,只有构成一棵树的n-1条边
最小生成树:各边代价之和最小的生成树
Prime算法:
思想:点集分为两个U,V-U, 分别(U)存树上的结点和(V-U)去掉U中结点的剩余结点;从V-U种选择代价最小的边
并入边集,直到U=V.这一算法不会构成回路, 因为并入的边始终是邻接点分布在U和V-U中.
//邻接矩阵
#define MAXV ...
typedef struct{
int no;//顶点编号
InfoType info;
}VertexType;
typedef struct{
int edges[MAXV][MAXV];//邻接矩阵
int n, e;//顶点数量, 边数
VertexType vexs[MAXV];//存放结点信息
}MatGraph;
#define M 32767
void Prime(MatGraph g, int v){
int lowcost[MAXV];
int min;
int closet[MAXV];
int i, j ,k;
for(i = 0; i < g.n; i++){
lowcost[i] = g.edges[v][i];
closet[i] = v;
}
for(i = 1; i < g.n; i++){
min = M;
for(j = 0; j < g.n; j++){
if(lowcost[j] != 0 && lowcost[j] < min){
min = lowcost[j];
k = j;
}
}
printf("边(%d, %d)权为: %d", v, k, lowcost[k]);
lowcost[k] = 0;
for(j = 0; j < g.n; j++){
if(lowcost[j] != 0 && lowcost[j] > g.edges[k][j])
{
lowcost[j] = g.edges[k][j];
closet[j] = k;
}
}
}
}
Kruskal
总结:对所有边的权重值按照升序排列,依次选取权重较小的边,
确保不构成回路(选中的边的所有结点不能重复出现在新加入的一条边中)
#define 32367
typedef struct{
int u;
int v;
int w;
//边的始点, 重点, 权重
}Edge;
void Kruskal(MatGraph h){
int i, j, u1, v1, sn1, sn2, k;
int vset[MAXV];
Edge E[MaxSize];
k = 0;//指示数组E的下标
//对于边(权重)初始化
for(i = 0; i < g.n; i++){
for(j = i + 1; j < g.n; j++)//上三角
if(g.edge[i][j] != M)//M = 32367
{
E[k].u = i; E[k].v = j;
E[k].w = g.edge[i][j]; k++;
}
}
//冒泡排序最简单
Sort(E, edge); //升序, 权值递增
for(i = 0; i < g.n; i++)//初始化辅助数组
vset[i] = i;
k = 1;//k:当前构造生成树的第几条边, 初始值为1
j = 0;//E中边的下标
while(k < g.n){
u1 = E[j].u;
v1 = E[j].v;
sn1 = vset[u1];
sn2 = vset[u2];
if(sn1 != sn2){
printf("(%d, %d): %d", u1, v1, E[j].w);
k++;
for(i = 0; i < g.n; i++){
if(vset[i] == sn2)
vset[i] = sn1;//改结点相同, 表示这两个结点已经形
//通路, 避免成环
}
j++;
}
}
}
拓扑排序
原理: 对于邻接矩阵
找入度为零的结点i, 即列为零的结点,标记, 将其删除, 删除邻接边即将序号i的行置零 , 重复...
邻接表
加一个辅助数组记录每个结点的入度; 入度为0, 入栈;
栈非空, 出栈, 删除邻接边;
即辅助数组邻接点的总数count-1;
//邻接表定义如下
typedef struct ANode{
int adjvex; // 该边的终点编号
struct Anode *nextarc;
}ArcNode;
typedef struct{
Vertex data;
int count;//辅助数组, 存放入度
ArcNode *firstarc;
}VNode;
typedef struct{
VNode adjlist[MAV];
int n, e;
}AdjGraph;
拓扑排序
//基于邻接表
void TopSort(AdjGraph *G){
int i, j;
int st[MAXV], top = -1;//栈的指针
ArcNode *p;
for(i = 0; i < G->n;i++){//入度置初值为0
G->adjlist[i].count = 0;
}
for(i = 0; i < G->n; i++){//求所有顶点的入度
p = G->adjlist[i].firstarc;
while(p!=NULL){
G->adjlist[p->adjvex].count++;
p = p->nextarc;//邻接表
}
}
for(i = 0; i < G.n; i++){//入度为零进栈
if(G->adjlist[i].count == 0){
top++;
st[top] = i;
}
}
while(top != -2){
i = st[top]; top--;//出栈一个顶点i
printf("%d", i);
p = G->adjlist[i].firstarc;
while(p != NULL){
j = p->adjvex;//找出p的邻接点,入度-1
G->adjlist[j].count--;
if(G->adjlist[j].count == 0){//入度为0入栈
top++;
st[top] = j;
}
p = p->nextarc;
}
}
}
AOE网--边表示活动的网
唯一入度为0的顶点为源点,唯一出度为0的顶点为汇点
源点到汇点的最长路径长度为整个工程任务所需时间,称为关键路径
关键路径上的活动称为关键活动.
//迪杰斯特算法 看PPT,难度较大
void Dijkstra(MatGraph g, int v){ // v is the original dot.
int dist[MAXV], path[MAXV]; //dist is to memorize the weighted length of the path.
//path is used to memorize the node of the path
int s[MAXV]; //to mark the node whether have been used.
int mindist, i, j;
for(i = 0; i < g.n; i++){
dist[i] = g.edges[v][i]; //Firstly store the weighted length of the related edge.
//If it is not relevant to it, put infinite on it. If it is itself, put 0 on it
s[i] = 0;//s[] is cleared.
if(g.edge[v][i] < INF)//如果v和i邻接那么赋值i的邻接点为v
path[i] = v;
else
path[i] = -1;//path is used to memorize the node before the latest
//if it is -1, proven there is no path.
s[v] =1;
for(i = 0; i < g.n; i++){//找出路径长度最小顶点u
mindis = INF;
for(j = 0; j < g.n; j++)//Find out the shortest length of the node.
if(s[j] == 0 && dist[j] < mindis){
u = j;