while(!x)相当于x!=1;
while(x)相当于x==1;
一,图的存储方法;
1邻接矩阵存储方法:
1.1类型定义:
#define MAXV 100
typedef struct {
int no;//定点编号
char info;//定点其他信息
}VertexType;//定点定义
typedef struct {
int adjMatrix[MAXV][MAXV];//邻接矩阵
int vexnum,arcnum;//定点数,弧数
VertexType Vexs[MAXV];//存放定点信息
}MGragh;//图定义
1.2用邻接矩阵创建带权无向图:
- 算法思路
- 1.输入总顶点数和总边数(为vexnum和arcnum赋值)
- 2.依次输入点的信息存入顶点表中(为vexs[i]赋值)。
- 3.初始化邻接矩阵,使每个权值初始化为极大值。
- 4.构造邻接矩阵
#include <stdio.h>
#include <string.h>
#define Max 999999//无穷大
MGragh CreateAdjMatrix(){
MGragh G;
int v1,v2,w;
scanf("%d %d",&G.vexnum,&G.arcnum);//输入总顶点数,总边数
for(int i=0;i<G.vexnum;i++){
scanf("%c",&G.Vexs[i]);//依次输入顶点的信息
getchar();
}
for(int i=0;i<G.vexnum;i++){
for(int j=0;j<G.vexnum;j++){
G.adjMatrix[i][j]=Max;//边的权值均置为无穷大
}
}
for(int k=0;k<G.arcnum;k++){
scanf("%d %d %d",&v1,&v2,&w);//输入一条边所依附的顶点及边的权值
G.adjMatrix[v1][v2]=w; G.adjMatrix[v2][v1]=w;//边<v1,v2>的权值为w,边<v2,v1>的权值为w
}
return (G);
}
1.3输出:
void PrintMG(MGraph MG)// 输出邻接矩阵
{
int i, j;
for (i = 0; i < MG.vexnum; i++) {
for (j = 0; j < MG.vexnum; j++)
printf("%d", MG.edges[i][j]);
printf("\n");
}
}
2.邻接表存储方法
2.1类型定义:
#include<string.h>
#include<stdio.h>
#include<stdlib.h>
#define MAXV 9999
typedef struct ANode{
int adjvex;//该弧的终点位置
struct ANode *nextarc;//指向下一条弧的指针
char info;//该弧的相关信息可为weight
}ArcNode;//定义弧结点
typedef struct Vnode{
char data;//顶点域,存储顶点信息
ArcNode *firstarc;//指向第一条弧的表头指针
} VNode;//定义结点
typedef struct {
VNode AdjList[MAXV];//邻接表
int Vernum,arcnum;//定点数,边数
int kind;//图的种类标志
}ALGraph;//定义图类型
2.2用邻接表构造无权图
- 算法思路
- 1.输入总顶点数和总边数(为vexnum和arcnum赋值)
- 2.依次输入点的信息存入顶点表中(为AdjList[i].data赋值)。同时初始化邻接表,使每个结点的头指针初始化为NULL。
- 4.构造邻接表:输入每一条边的两个定点v1,v2。利用中间弧结点s(将s的终点置为v2,s->info赋值为G.AdjList[v2].data),再将s头插进G.AdjList[v1]。(无向图还要构造v1)
ALGraph CreatAdjList(ALGraph G){
scanf("%d %d",&G.Vernum,&G.arcnum);//输入图中顶点总数,弧总数
scanf("%d",&G.kind);getchar();//kind为0则G为无向图,若kind为1则G为有向图
for(int i=0;i<G.Vernum;i++){
scanf("%c",&G.AdjList[i].data);//输入每个定点的相关信息
getchar();
G.AdjList[i].firstarc=NULL;//每个定点得头指针置为NULL
}
for(int k=0;k<G.arcnum;k++){
int v1,v2;scanf("%d %d",&v1,&v2);//输入无向图中每一条弧的两个定点
ArcNode *s=(ArcNode*)malloc(sizeof(ArcNode));
s->adjvex=v2;//弧结点s的终点结点为v2
s->info=G.AdjList[v2].data;
s->nextarc=G.AdjList[v1].firstarc;//头插
G.AdjList[v1].firstarc=s;//头插
if(G.kind==0){//无向图的对称性
ArcNode *t;
t=(ArcNode*)malloc(sizeof(ArcNode));
t->adjvex=v1;
t->info=G.AdjList[v1].data;
t->nextarc=G.AdjList[v2].firstarc;
G.AdjList[v2].firstarc=t;
}
}
return (G);
}
2.3输出:
void showALGraph(ALGraph *G){
for(int v=0;v<G->Vernum;v++){
printf("%c",G->AdjList[v].data);
ArcNode *p=G->AdjList[v].firstarc;
while(p){
printf("%c",p->info);
p=p->nextarc;
}
printf("\n");
}
}
二,图的遍历
1.深度优先遍历DFS
算法思想:借助visited[MAXV]={0}数组
- 从起点开始,访问与其相邻的第一个未被访问的节点。
- 如果该节点未被访问,则将其标记为已访问(visited=1)(并将其加入到栈中)。
- 继续访问该节点的下一个未被访问的相邻节点,重复步骤2。
- 如果该节点没有未被访问的相邻节点,(则从栈中弹出该节点)并返回到上一个节点,重复步骤3。
1.1以邻接表为存储结构的DFS
// 深度优先遍历
void DFS(ALGraph G,int v,int visited[]){
visited[v]=1;// 标记当前顶点已经访问过
printf("%d ",G.AdjList[v].data);
ArcNode *p=G.AdjList[v].firstarc;
while(p!=NULL){
if(visited[p->adjvex]!=1){
//如果邻接顶点未被访问过,则递归调用DFS函数继续遍历
DFS(G, p->adjvex,visited);
}
p=p->nextarc;
}
}
int main(){
ALGraph G;
G=CreatAdjList(G);
int d;scanf("%d",&d);
int visited[MAXV]={0};
DFS(G,d-1,visited);
return 0;
}
1.2以邻接矩阵为存储结构的DFS
void DFS(MGragh G,int v,int visited[]){
visited[v]=1;//标记已经访问的结点
printf("%d ",G.Vexs[v].no);
for(int w=0;w<G.vexnum;w++){
if(G.adjMatrix[v][w]&&visited[w]!=1)DFS(G,w,visited);
}
}
int main(){
MGragh G;
G=CreateAdjMatrix();
int visited[MAXV]={0};
int v0;scanf("%d",&v0);//从V0开始遍历
DFS(G,v0-1,visited);
return 0;
}
2.广度优先遍历BFS
算法思想:借助visited[MAXV]数组和队列
- 从起点开始,将起点加入到队列中。
- 从队列中取出队首元素,并访问与其相邻的所有未被访问的节点。
- 将这些节点标记为已访问,并将其加入到队列中。
- 重复步骤2和3,直到队列为空。
2.1以邻接表为存储结构的BFS
// 广度优先遍历
void BFS(ALGraph G, int v, int visited[]) {
SqQueue Q; // 定义队列
Q=initSqQueue();
printf("%d ", G.AdjList[v].data); // 访问初始顶点v并打印顶点值
visited[v] = 1; // 标记顶点v已访问
Q.data[Q.rear++] = v; // 顶点v入队
while (Q.front != Q.rear) { // 队列不空
int j = Q.data[Q.front++]; // 出队
ArcNode *p = G.AdjList[j].firstarc;
while (p != NULL) {
if (visited[p->adjvex]!=1) {//结点未被访问
printf("%d ", G.AdjList[p->adjvex].data); // 打印顶点值
visited[p->adjvex] = 1; // 标记顶点已访问
Q.data[Q.rear++] = p->adjvex; // 该顶点入队
}
p = p->nextarc;
}
}
}
int main(){
ALGraph G;
G=CreatAdjList(G);
int d;scanf("%d",&d);//输入遍历初始点d
int visited[MAXV]={0};
BFS(G,d-1,visited);
return 0;
}
typedef struct{
int data[MAXV+1];
int front,rear;
}SqQueue;//队列
SqQueue initSqQueue(){//初始化队列
SqQueue q;
q.front=0;
q.rear=0; // 队头和队尾指针
return q;
}
三,最小生成树
G(n,e) → MST:1️⃣n个顶点,n-1条边;2️⃣连通无回路;3️⃣权值最小;
1.prim算法
1.1算法思想:
U和V-U两个阵营中不停的找一条最短的(代价最低的)可连通的边,然后将该边附着的在V-U阵营中的顶点加入U阵营中。
1.2 代码
void MiniSpanTree_Prim(MGraph *G,Vertex start){
ShortEdge ShortEdge[VexMAx];
int vset[G->vexnum];//辅助数组记录顶点位置未访问0访问1
int k=LocateVex(G,start);//定位初始点的位置
for(int i=0;i<G->vexnum;i++){
if(i!=k){//非k的顶点放入v-u的集合
ShortEdge[i].adjvex=start;//候选最短边的邻接点初置为start
ShortEdge[i].lowcost=G->AdjMatrix[k][i];
vset[i]=0;
}
}
vset[k]=1;//lowcost为1表示该顶点属于U集合
for(int i=0;i<G->vexnum;i++){//找最短路径的顶点
int min=-1;
for(int j=0;j<G->vexnum;j++){
if(vset[j]!=1&&ShortEdge[j].lowcost<min){
min=ShortEdge[j].lowcost;
k=j;//求出T的下一个结点:第k个结点
}
}
vset[k]=1;//第k个定点并入U集合
for(int j=0;j<G->vexnum;j++){//有更短路径出现时,将其替换进shortedge数组
if((vset[j]==0)&&(G->AdjMatrix[k][j]<ShortEdge[j].lowcost)){//有更短路径出现时,将其替换进shortedge数组
ShortEdge[j].adjvex=G->vexs[k];
ShortEdge[j].lowcost=G->AdjMatrix[k][j];
}
}
}
}
typedef struct{//辅助数组结构体(候选最短边)
Vertex adjvex;//候选最短边的邻接点
int lowcost;//候选最短边的权值
}ShortEdge;
int LocateVex(MGraph *G,Vertex v1){//查找元素v在一维数组 Vertex[] 中的下标,并返回下标
int flag=-1;
for(int i=0;i<G->vexnum;i++){
if(v1.info==G->vexs[i].info)flag=i;
}
return flag;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define VexMAx 100//最大顶点数为100
#define MaxInt 9999//表示最大整数,表示 ∞
typedef struct{
int no;
char info;
}Vertex;
typedef struct{
int AdjMatrix[VexMAx][VexMAx];
int vexnum,arcnum;
Vertex vexs[VexMAx];
}MGraph;
void CreateAdjMatrix(MGraph *G);
int LocateVex(MGraph *G,Vertex v);
void print(MGraph G);
void MiniSpanTree_Prim(MGraph *G,Vertex start);
int main(){
MGraph G;
CreateAdjMatrix(&G);
print(G);
printf("请输入起始点:");Vertex start;
scanf("%c",&start.info);
MiniSpanTree_Prim(&G,start);
}
void CreateAdjMatrix(MGraph *G){//构造以邻接矩阵为存储结构的无向图
printf("输入顶点个数和边数:\n");
printf("顶点数 n=");
scanf("%d",&G->vexnum);
printf("边 数 e=");
scanf("%d",&G->arcnum);
printf("\nvexnum=%d\n",G->vexnum);
//2.输入顶点元素
printf("输入顶点元素:");
for(int k=0;k<G->vexnum;k++){
printf("%d是 ",k);
scanf("%c",&G->vexs[k].info);
G->vexs[k].info=getchar();
}
for(int i=0;i<G->vexnum;i++){
printf("%c",G->vexs[i].info);
}
//3.矩阵初始化
for(int i=0;i<G->vexnum;i++)
for(int j=0;j<G->vexnum;j++){
G->AdjMatrix[i][j]=MaxInt;
}
//4.构建邻接矩阵
int a,b,weight;//v1->v2的权值
Vertex v1,v2;
printf("请输入边的信息和权值(a b 9):\n");
for(int i=0;i<G->arcnum;i++){
scanf("%c %c %d",&v1.info,&v2.info,&weight);
//printf("\nv1.info=%c v2.info=%c weight=%d\n",v1.info,v2.info,weight);
a=LocateVex(G,v1); //获取v1所对应的在Vertex数组中的坐标
b=LocateVex(G,v2); //获取v2所对应的在Vertex数组中的坐标
if(a==-1||b==-1)printf("NO This Vertex!\n");
}
G->AdjMatrix[a][b]=weight;
G->AdjMatrix[b][a]=weight;//无向网仅此处不同
}
void print(MGraph G){
int i,j;
printf("\n-------------------------------");
printf("\n 邻接矩阵:\n\n");
printf("\t ");
for(i=0;i<G.vexnum;i++)
printf("\t%c",G.vexs[i]);
printf("\n");
for(i=0;i<G.vexnum;i++){
printf("\t%c",G.vexs[i]);
for(j=0;j<G.vexnum;j++){
if(G.AdjMatrix[i][j]==MaxInt) printf("\t∞");
else printf("\t%d",G.AdjMatrix[i][j]);
}
printf("\n");
}
}
四,最短路径
五,拓扑排序