图的概念以及基本算法实现
这里主要参考 作用太大了销夜
最小生成树算法的相关变形题
先从最简单的并查集实现kruskal算法写起
并查集表示:
typedef struct Edge{//并查集的边节点 front,to为边的指向,w代表权值
int front;
int to;
int w;
}Edge;
找到所属根节点:
int father[maxsize] = {0}; //所属根节点
//查找根节点
int Find(int x){ //递归找到所属根节点
if(father[x]<=0){ //找到了
return x;
} //递归查找根节点
return father[x] = Find(father[x]);
}
按秩合并:
void Union(int x, int y){ //按秩合并两个并查集 秩:当前节点深度乘以-1
int fx = Find(x);
int fy = Find(y);
if(fx == fy) return; //两者属于同一个并查集,无需合并
if(father[fx] < father[fy]){//fy深度 小于 fx深度 ,fy合并到fx中
father[fy] = fx;
}
else{ //否则 fx合并到 fy中
if(father[fx] == father[fy]){ //秩相同时,合并深度加1 ,秩要减1,
father[fy]--;
}
father[fx] = fy;
}
}
对边进行排序: 实现从图到并查集的转换,同时使用冒泡排序边结构体进行排序
int SortEdge(int graph[][maxsize], int n, Edge edge[]){ //实现两个功能,将图转化为并查集表示,同时对并查集进行排序
int edgenum = 0; //边的个数
for(int i = 0; i <= n; i++) //无向图遍历上半矩阵即可
for(int j = i+1; j <= n; j++)
if(graph[i][j]){ //存在边,赋值
edgenum++;
edge[edgenum].front = i;
edge[edgenum].to = j;
edge[edgenum].w = graph[i][j];
}
//冒泡排序对边进行排序
for(int i = edgenum; i > 1; i--){
int flag = 0;
for(int j =1; j < i; j++){
if(edge[j].w > edge[j+1].w){ //递增排序 求最大生成树只需要改为递减排序即可
Edge temp = edge[j];
edge[j] = edge[j+1];
edge[j+1] = temp;
flag = 1; //本趟进行了交换
}
}
if(flag == 0){ //该趟未排序,已经有序
break;
}
}
return edgenum;
}
上述代码基本上实现了一个并查集的各个功能,下面实现最小生成树算法
思路:
代码:
//基础算法:求最小生成树
void Kruskal_MinTree(int graph[][maxsize], int n){
Edge edge[maxsize];
int edgenum = SortEdge(graph,n,edge); //对当前图进行构造并查集,并排序
int setnum = n; //连通分量个数,最开始为n个顶点,所以有n个
int k = 1;
int ans = 0;
while(setnum > 1){ //最小生成树的算法实现
int x = edge[k].front;
int y = edge[k].to;
int w = edge[k].w; //不断取最小的边,判断是否处于同一个并查集,不同就加入
k++;
if(Find(x) == Find(y)) continue;
Union(x,y); //属于不同并查集,合并
setnum--;
ans += w;
printf("%d--%d:%d ", x, y, w);
}
printf("最小生成树的权值为%d", ans);
}
全部代码: 实现最小生成树
//最小生成树算法
typedef struct Edge{//并查集的边节点 front,to为边的指向,w代表权值
int front;
int to;
int w;
}Edge;
int father[maxsize] = {0}; //所属根节点
//查找根节点
int Find(int x){ //递归找到所属根节点
if(father[x]<=0){ //找到了
return x;
} //递归查找根节点
return father[x] = Find(father[x]);
}
//按秩合并
void Union(int x, int y){ //按秩合并两个并查集 秩:当前节点深度乘以-1
int fx = Find(x);
int fy = Find(y);
if(fx == fy) return; //两者属于同一个并查集,无需合并
if(father[fx] < father[fy]){//fy深度 小于 fx深度 ,fy合并到fx中
father[fy] = fx;
}
else{ //否则 fx合并到 fy中
if(father[fx] == father[fy]){ //秩相同时,合并深度加1 ,秩要减1,
father[fy]--;
}
father[fx] = fy;
}
}
//对边进行排序
int SortEdge(int graph[][maxsize], int n, Edge edge[]){ //实现两个功能,将图转化为并查集表示,同时对并查集进行排序
int edgenum = 0; //边的个数
for(int i = 0; i <= n; i++) //无向图遍历上半矩阵即可
for(int j = i+1; j <= n; j++)
if(graph[i][j]){ //存在边,赋值
edgenum++;
edge[edgenum].front = i;
edge[edgenum].to = j;
edge[edgenum].w = graph[i][j];
}
//冒泡排序对边进行排序
for(int i = edgenum; i > 1; i--){
int flag = 0;
for(int j =1; j < i; j++){
if(edge[j].w > edge[j+1].w){ //递增排序 求最大生成树只需要改为递减排序即可
Edge temp = edge[j];
edge[j] = edge[j+1];
edge[j+1] = temp;
flag = 1; //本趟进行了交换
}
}
if(flag == 0){ //该趟未排序,已经有序
break;
}
}
return edgenum;
}
//基础算法:求最小生成树
void Kruskal_MinTree(int graph[][maxsize], int n){
Edge edge[maxsize];
int edgenum = SortEdge(graph,n,edge); //对当前图进行构造并查集,并排序
int setnum = n; //连通分量个数,最开始为n个顶点,所以有n个
int k = 1;
int ans = 0;
while(setnum > 1){ //最小生成树的算法实现
int x = edge[k].front;
int y = edge[k].to;
int w = edge[k].w; //不断取最小的边,判断是否处于同一个并查集,不同就加入
k++;
if(Find(x) == Find(y)) continue;
Union(x,y); //属于不同并查集,合并
setnum--;
ans += w;
printf("%d--%d:%d ", x, y, w);
}
printf("最小生成树的权值为%d", ans);
}
思路: 在加入新边的环中找到最大的那一条边,删除最大的就是当前的最小生成树
前面的找父节点、排序算法不变,后序的最小生成树的算法也是
添加一个算法DFS寻找当前新加入边所形成的环中最大的那一条边
添加一个加入新边求最小生成树的函数
kruskal函数基本不变
DFS找最大边:
int mintree[maxsize][maxsize] = {0}; //领接矩阵存储最小生成树
//
//在用图存储的最小生成树中找到新形成的环中,除新加入的边的最大值
void DFS_FindMostLong(int v, int b, int n,int visited[], int curw, int &maxw, Edge &maxe){
//v顶点为新加入的边的起始节点,b为结尾顶点,n为环中边的个数,visited标记是否访问该边,
//curw存储当前最大权值,maxw为需要找到的最大权值,maxe保存最大边
visited[v] = 1;
if(v == b){ //已经找完了
maxw = curw;
return;
}
for(int i = 0; i <= n; i++){
if(mintree[v][i] != 0 && !visited[i]){ //遍历未被访问过的边
int temp = curw; //用于回溯
if(mintree[v][i] > curw){ //记录更大的边
curw = mintree[v][i];
maxe.front = v;
maxe.to = i;
}
DFS_FindMostLong(i,b,n,visited,curw,maxw,maxe);
curw = temp; //回溯
}
}
}
加入新边判断是否需要更新最小生成树
//加入新边找到最小生成树
int AddNewEdge(Edge newe, int ans, int n){
int visited[maxsize] ={0};
int maxw; //存储最长边
Edge maxe;
DFS_FindMostLong(newe.front, newe.to,n,visited, 0, maxw, maxe);
if(newe.w < maxw){ //新加入的边的权值如果小于当前环中最长边的权值,替换
ans = ans - maxw + newe.w;
mintree[maxe.front][maxe.to] = 0; //删除最长的那条边
mintree[maxe.to][maxe.front] = 0;
mintree[newe.front][newe.to] = newe.w; //加入新边
mintree[newe.to][newe.front] = newe.w;
}
return ans;
}
整体实现代码:
typedef struct Edge{//并查集的边节点 front,to为边的指向,w代表权值
int front;
int to;
int w;
}Edge;
int father[maxsize] = {0}; //所属根节点
//查找根节点
int Find(int x){ //递归找到所属根节点
if(father[x]<=0){ //找到了
return x;
} //递归查找根节点
return father[x] = Find(father[x]);
}
//按秩合并
void Union(int x, int y){ //按秩合并两个并查集 秩:当前节点深度乘以-1
int fx = Find(x);
int fy = Find(y);
if(fx == fy) return; //两者属于同一个并查集,无需合并
if(father[fx] < father[fy]){//fy深度 小于 fx深度 ,fy合并到fx中
father[fy] = fx;
}
else{ //否则 fx合并到 fy中
if(father[fx] == father[fy]){ //秩相同时,合并深度加1 ,秩要减1,
father[fy]--;
}
father[fx] = fy;
}
}
//进阶算法1:加入一条新边,求最小生成树
int mintree[maxsize][maxsize] = {0}; //领接矩阵存储最小生成树
//
//在用图存储的最小生成树中找到新形成的环中,除新加入的边的最大值
void DFS_FindMostLong(int v, int b, int n,int visited[], int curw, int &maxw, Edge &maxe){
//v顶点为新加入的边的起始节点,b为结尾顶点,n为环中边的个数,visited标记是否访问该边,
//curw存储当前最大权值,maxw为需要找到的最大权值,maxe保存最大边
visited[v] = 1;
if(v == b){ //已经找完了
maxw = curw;
return;
}
for(int i = 0; i <= n; i++){
if(mintree[v][i] != 0 && !visited[i]){ //遍历未被访问过的边
int temp = curw; //用于回溯
if(mintree[v][i] > curw){ //记录更大的边
curw = mintree[v][i];
maxe.front = v;
maxe.to = i;
}
DFS_FindMostLong(i,b,n,visited,curw,maxw,maxe);
curw = temp; //回溯
}
}
}
//加入新边找到最小生成树
int AddNewEdge(Edge newe, int ans, int n){
int visited[maxsize] ={0};
int maxw; //存储最长边
Edge maxe;
DFS_FindMostLong(newe.front, newe.to,n,visited, 0, maxw, maxe);
if(newe.w < maxw){ //新加入的边的权值如果小于当前环中最长边的权值,替换
ans = ans - maxw + newe.w;
mintree[maxe.front][maxe.to] = 0; //删除最长的那条边
mintree[maxe.to][maxe.front] = 0;
mintree[newe.front][newe.to] = newe.w; //加入新边
mintree[newe.to][newe.front] = newe.w;
}
return ans;
}
//算法实现
int Kruskal_AddNewEdge(int graph[][maxsize], int n, Edge newe){
Edge edge[maxsize];
int edgenum = SortEdge(graph,n,edge);
int setnum = n;
int k = 1;
int ans = 0;
while(setnum > 1){
int x = edge[k].front;
int y = edge[k].to;
int w = edge[k].w;
k++;
if(Find(x) == Find(y)) continue;
Union(x,y);
setnum--;
ans += w;
mintree[x][y] = mintree[y][x] = w; //利用矩阵保存最小生成树
}
ans = AddNewEdge(newe,ans,n);
printf("新的最小生成树的最小权值为%d",ans);
return ans;
}
思路:
代码:
typedef struct Edge{//并查集的边节点 front,to为边的指向,w代表权值
int front;
int to;
int w;
}Edge;
int father[maxsize] = {0}; //所属根节点
//查找根节点
int Find(int x){ //递归找到所属根节点
if(father[x]<=0){ //找到了
return x;
} //递归查找根节点
return father[x] = Find(father[x]);
}
//按秩合并
void Union(int x, int y){ //按秩合并两个并查集 秩:当前节点深度乘以-1
int fx = Find(x);
int fy = Find(y);
if(fx == fy) return; //两者属于同一个并查集,无需合并
if(father[fx] < father[fy]){//fy深度 小于 fx深度 ,fy合并到fx中
father[fy] = fx;
}
else{ //否则 fx合并到 fy中
if(father[fx] == father[fy]){ //秩相同时,合并深度加1 ,秩要减1,
father[fy]--;
}
father[fx] = fy;
}
}
//对边进行排序
int SortEdge(int graph[][maxsize], int n, Edge edge[]){ //实现两个功能,将图转化为并查集表示,同时对并查集进行排序
int edgenum = 0; //边的个数
for(int i = 0; i <= n; i++) //无向图遍历上半矩阵即可
for(int j = i+1; j <= n; j++)
if(graph[i][j]){ //存在边,赋值
edgenum++;
edge[edgenum].front = i;
edge[edgenum].to = j;
edge[edgenum].w = graph[i][j];
}
//冒泡排序对边进行排序
for(int i = edgenum; i > 1; i--){
int flag = 0;
for(int j =1; j < i; j++){
if(edge[j].w > edge[j+1].w){ //递增排序 求最大生成树只需要改为递减排序即可
Edge temp = edge[j];
edge[j] = edge[j+1];
edge[j+1] = temp;
flag = 1; //本趟进行了交换
}
}
if(flag == 0){ //该趟未排序,已经有序
break;
}
}
return edgenum;
}
//在用图存储的最小生成树中找到新形成的环中,除新加入的边的最大值
void DFS_FindMostLong(int v, int b, int n,int visited[], int curw, int &maxw, Edge &maxe){
//v顶点为新加入的边的起始节点,b为结尾顶点,n为环中边的个数,visited标记是否访问该边,
//curw存储当前最大权值,maxw为需要找到的最大权值,maxe保存最大边
visited[v] = 1;
if(v == b){ //已经找完了
maxw = curw;
return;
}
for(int i = 0; i <= n; i++){
if(mintree[v][i] != 0 && !visited[i]){ //遍历未被访问过的边
int temp = curw; //用于回溯
if(mintree[v][i] > curw){ //记录更大的边
curw = mintree[v][i];
maxe.front = v;
maxe.to = i;
}
DFS_FindMostLong(i,b,n,visited,curw,maxw,maxe);
curw = temp; //回溯
}
}
}
//添加新边找到最小生成树
int AddNewEdge(Edge newe, int ans, int n){
int visited[maxsize] ={0};
int maxw; //存储最长边
Edge maxe;
DFS_FindMostLong(newe.front, newe.to,n,visited, 0, maxw, maxe);
printf("在原先的最小生成树中,节点%d到节点%d的路径上的最长边的权值为:%d\n", maxe.front, maxe.to, maxw);
ans = ans - maxw + newe.w;
printf("加入边 %d--%d 后,新的最小生成树的权值为:%d\n\n", maxe.front, maxe.to, ans);
return ans;
}
//找到最小生成树
int Kruskal_SecTree(int graph[][maxsize], int n){
edgenum = SortEdge(graph,n,edge);
int setnum = n;
int k = 1;
int ans = 0;
while(setnum > 1){
int x = edge[k].front;
int y = edge[k].to;
int w = edge[k].w;
k++;
if(Find(x) == Find(y)) continue;
Union(x,y);
setnum--;
ans += w;
mintree[x][y] = mintree[y][x] = w; //利用矩阵保存最小生成树
isuse[k-1] = 1; //记录使用过的边 前面已经k++,所以这里是k-1
}
return ans;
}
int SecondTree(int graph[][maxsize], int n){
int ans = Kruskal_SecTree(graph,n);
int newans = 100000; //保存次小生成树的权值
int min; //保存次小生成树下标
for(int i = 1; i <= edgenum; i++){
if(!isuse[i]){
int temp = AddNewEdge(edge[i],ans,n); //计算加入新边最小权值
if(temp < newans){
newans = temp; //保存最小权值以及对应最小边
}
}
}
printf("次小生成树的权值为%d",newans);
return newans;
}
思路:
代码:
typedef struct Edge{//并查集的边节点 front,to为边的指向,w代表权值
int front;
int to;
int w;
}Edge;
int father[maxsize] = {0}; //所属根节点
//查找根节点
int Find(int x){ //递归找到所属根节点
if(father[x]<=0){ //找到了
return x;
} //递归查找根节点
return father[x] = Find(father[x]);
}
//按秩合并
void Union(int x, int y){ //按秩合并两个并查集 秩:当前节点深度乘以-1
int fx = Find(x);
int fy = Find(y);
if(fx == fy) return; //两者属于同一个并查集,无需合并
if(father[fx] < father[fy]){//fy深度 小于 fx深度 ,fy合并到fx中
father[fy] = fx;
}
else{ //否则 fx合并到 fy中
if(father[fx] == father[fy]){ //秩相同时,合并深度加1 ,秩要减1,
father[fy]--;
}
father[fx] = fy;
}
}
//对边进行排序
int SortEdge(int graph[][maxsize], int n, Edge edge[]){ //实现两个功能,将图转化为并查集表示,同时对并查集进行排序
int edgenum = 0; //边的个数
for(int i = 0; i <= n; i++) //无向图遍历上半矩阵即可
for(int j = i+1; j <= n; j++)
if(graph[i][j]){ //存在边,赋值
edgenum++;
edge[edgenum].front = i;
edge[edgenum].to = j;
edge[edgenum].w = graph[i][j];
}
//冒泡排序对边进行排序
for(int i = edgenum; i > 1; i--){
int flag = 0;
for(int j =1; j < i; j++){
if(edge[j].w > edge[j+1].w){ //递增排序 求最大生成树只需要改为递减排序即可
Edge temp = edge[j];
edge[j] = edge[j+1];
edge[j+1] = temp;
flag = 1; //本趟进行了交换
}
}
if(flag == 0){ //该趟未排序,已经有序
break;
}
}
return edgenum;
}
//判断最小生成树是否唯一
bool Kruskal_Isunique(int graph[][maxsize], int n) {
edgenum = SortEdge(graph,n,edge);
int setnum = n;
int k = 1;
int ans = 0;
while (setnum > 1) { //连通分量个数大于1 构造最小生成树的过程
int x = edge[k].front;
int y = edge[k].to;
int w = edge[k].w;
k++;
if (Find(x) == Find(y)) continue; //这条边的两个顶点从属于一个集合舍弃这条边
for (int i = k; i <= edgenum; i++) {
if (edge[i].w == w) { //如果这条新边的权值与刚才那条边权值相同
int newa = edge[i].front;
int newb = edge[i].to;
//若两边的所属连通块相同,说明最小生成树不唯一;需注意此时还没有将a,b为顶点的边合并
if (Find(x) == Find(newa) && Find(y) == Find(newb) || Find(x) == Find(newb) && Find(y) == Find(newa)){
printf("边 %d--%d 与边 %d-- %d 可任意选,最小生成树不唯一\n", x, y, newa, newb);
return false;
}
}
}
Union(x, y); //已经确保该条边没有可替代的边,将这条边合并
setnum--; //合并之后连通分量-1
ans += w;
}
return true;
}
思路:
代码:
typedef struct ArcNode{ //边结点
int adjvex;
struct ArcNode *next;
}ArcNode;
typedef struct VNode{ //顶点节点
int data;
struct ArcNode *firstarc;
}VNode;
typedef struct AGraph{ //领接表
VNode adjlist[maxsize];
int vexnum, edgenum;
}AGraph;
//领接表的广度优先遍历
int *dfsAGraph(AGraph *G){
int n = G->vexnum;
int *in = (int *)malloc(sizeof(int)*n);
for(int i=0; i < n; i++){
in[i] = 0;
}
for(int i=0; i < G->vexnum;i++){
ArcNode *p = G->adjlist[i].firstarc;
while(p!=NULL){
in[p->adjvex] += 1;
p = p->next;
}
}
return in;
}
思路:
代码:
typedef struct ArcNode{ //边节点
int adjvex;
struct ArcNode *next;
}ArcNode;
typedef struct VNode{ //顶点节点
int data;
struct ArcNode *firstarc;
}VNode;
typedef struct AGraph{ //领接表
VNode adjlist[maxsize];
int vexnum, edgenum;
}AGraph;
int MaxLenBFS(AGraph *G, int v, int dist[]){
int visited[maxsize] = {0};
int queue[maxsize];
int front = -1, rear = -1,i,k,temp,max=0;
ArcNode *p = G->adjlist[v].firstarc;
for(i = 0; i < G->vexnum; i++){
dist[i] = -1;
}
queue[++rear] = v;
visited[v] = 1;
dist[v] = 0;
while(rear != front){
k = queue[++front];
p = G->adjlist[k].firstarc;
while(p!=NULL){
temp = p->adjvex;
if(visited[temp]==0){
queue[++rear] = temp;
visited[temp] = 1;
dist[temp] = dist[k] + 1;
}
p = p->next;
}
}
for(i=0; i < G->vexnum; i++){
if(dist[i]>dist[max]){
max = i;
}
}
return max; //返回端点
}
int Diameter(AGraph *G){
int dist[maxsize];
int first = MaxLenBFS(G,0,dist);
int last = MaxLenBFS(G,first,dist);
printf("直径为:%d",dist[last]);
return dist[last]; //返回直径长度
}
请设计一个算法判断无向图G是否为一棵树,若是树,返回1,否则返回0
思路
判断一个图是否为树需要两个条件
实现:
代码:
//思路:若该图为数,则该图为连通图,且顶点数-1 = 边数,深度优先
typedef struct MGraph{
int vex[maxsize];
int edge[maxsize][maxsize];
int vexnum, edgenum;
}MGraph;
int visited[maxsize] = {0};
void DFS(MGraph *G, int v, int &vnum, int &anum){
visited[v] = 1;
vnum++;
for(int i = 0; i < G->vexnum; i++){
if(G->edge[v][i] == 1){
anum++;
if(visited[i] == 0){
DFS(G,i,vnum,anum);
}
}
}
}
bool judgetree(MGraph *G){
int vnum = 0, anum = 0;
DFS(G,0,vnum,anum);
if(vnum == G->vexnum && 2*(vnum-1) == anum){
printf("是一棵树");
return true;
}
else{
printf("不是一棵树");
return false;
}
}
思路:
代码:
typedef struct ArcNode{ //边结点
int adjvex;
struct ArcNode *next;
}ArcNode;
typedef struct VNode{ //顶点节点
int data;
struct ArcNode *firstarc;
}VNode;
typedef struct AGraph{ //领接表
VNode adjlist[maxsize];
int vexnum, edgenum;
}AGraph;
//思路:深度优先遍历回溯
int visited[maxsize] = {0};
int path[maxsize] = {0};
void dfsfindall(AGraph *G, int i, int j, int count){ //count统计路径长度
path[count++] = i;
visited[i] = 1;
if(i == j){
for(int k = 0; k < count; k++){
printf("经过了节点%d ",path[k]);
}
printf("\n");
}
ArcNode *p = G->adjlist[i].firstarc;
while(p!=NULL){
if(visited[p->adjvex] == 0){
dfsfindall(G,p->adjvex,j,count);
}
p = p->next;
}
visited[i] = 0; //回溯
}
N*N的矩阵a表示有向图的领接表,其中
d[i,j] = 1 节点i到节点j有边
d[i,j] = 0 节点i到节点j无边
思路:
代码:
int visited[N] = {0}; //判断是否访问过
int isout[N] = {0}; //判断是否输出
void dfsfindallnode(int a[N][N], int i, int k){
if(k == 0){ //函数的终止条件
if(isout[i] == 0){
printf("可以到达节点%d\n",i);
isout[i] = 1;
return; //结束当前层递归
}
}
for(int j = 0; j < N; j++){
if(a[i][j] == 1 && visited[j]==0){//如果可以到达这个点的话
visited[j]=1;
dfsfindallnode(a,j,k-1);
visited[j]=0; //回溯还原
}
}
}
若G是一个使用领接表存储的有向图,设计一个算法,利用深度优先遍历算法,对图中节点进行拓扑排序
思路:
代码:
typedef struct ArcNode{ //边结点
int adjvex;
struct ArcNode *next;
}ArcNode;
typedef struct VNode{ //顶点节点
int data;
struct ArcNode *firstarc;
}VNode;
typedef struct AGraph{ //领接表
VNode adjlist[maxsize];
int vexnum, edgenum;
}AGraph;
int visited[maxsize] = {0};
int topo[maxsize];
int t;
void dfs(AGraph *G, int v){
visited[v] = 1;
struct ArcNode *p = G->adjlist[v].firstarc;
while(p!=NULL){
if(visited[p->adjvex] == 0){
dfs(G,p->adjvex);
}
p =p->next;
}
topo[t--] = v; //深度最深的,是拓扑序列的最后一位,然后依次递减
}
void toposort(AGraph *G){
t = G->vexnum-1;
for(int i =0; i < G->vexnum; i++){
if(visited[i] == 0){
dfs(G,i);
}
}
}
1、给定连通图G和G中的一个丁点v,求G的支撑树,满足两个条件,
(1)T的根为v
(2)T的层次遍历恰好是以v为起点的G的某个广度优先遍历
思路:
代码:
typedef struct ArcNode{ //边结点
int adjvex;
int info;
struct ArcNode *next;
}ArcNode;
typedef struct VNode{ //表结点
int data;
ArcNode *firstarc;
}VNode;
typedef struct AGraph{ //领结表
VNode adjlist[maxsize];
int vexnum, edgenum;
}AGraph;
typedef struct TNode{ //保存层次遍历的结构与数据
int val,childnum;
struct TNode *child[maxsize];
}TNode;
TNode *bfs(AGraph *G, int v){ //多叉树的层次遍历,每一个图的节点都要重新申请一个树的节点来保存
TNode *queue[maxsize],*k;
int front = -1, rear = -1;
int visited[G->vexnum] = {0};
struct TNode *root = (struct TNode *)malloc(sizeof(struct TNode));
struct ArcNode *p;
root->val = v; //将根节点入队并标记访问数组
visited[v] = 1;
queue[++rear] = root;
while(rear != front){
k = queue[++front];
k->childnum = 0;
p = G->adjlist[k->val].firstarc;
while(p!=NULL){
if(visited[p->adjvex] == 0){
struct TNode *temp = (struct TNode *)malloc(sizeof(struct TNode));
temp->val = p->adjvex;
k->child[k->childnum++] = temp;
queue[++rear] = temp;
visited[p->adjvex] = 1;
}
p = p->next;
}
}
return root;
}
思路:
代码:
typedef struct ArcNode{ //领接边
int adjvex;
struct ArcNode *next;
}ArcNode;
typedef struct VNode{ //领接顶点
int data;
struct ArcNode *firstarc;
}VNode;
typedef struct AGraph{ //领接表
VNode adjlist[maxsize];
int vexnum, edgenum;
}AGraph;
//思路:层次遍历每一条边,然后把每条边的指向的节点改为顶点节点,其起始节点为其指向节点
void GetReserseAdjlist(AGraph *G, AGraph *R){ //G为领接链表,R为逆领接链表
R->edgenum = G->edgenum; //初始化逆领接链表
R->vexnum = G->vexnum;
for(int i = 0; i < G->vexnum; i++){
R->adjlist[i].data = G->adjlist[i].data;
R->adjlist[i].firstarc = NULL;
}
for(int i =0; i < G->vexnum; i++){ //遍历领接表
ArcNode *p = G->adjlist[i].firstarc;
while(p!=NULL){
struct ArcNode *temp = (struct ArcNode *)malloc(sizeof(struct ArcNode));
temp->adjvex = i; //指向换一下
temp->next = R->adjlist[p->adjvex].firstarc; //不带头结点的头插法
R->adjlist[p->adjvex].firstarc = temp;
p = p->next; //继续往下找
}
}
}
思路:
先理解偏心距:偏心距指的是其他顶点到某个顶点中的最大值
中心:偏心距最小
先求出每个顶点到其他顶点的最短路径,然后从这些顶点中找到最大值 作为偏心距,然后在从这些偏心距中最小的作为中心
代码:
typedef struct ArcNode{ //边结点
int adjvex;
int info;
struct ArcNode *next;
}ArcNode;
typedef struct VNode{ //表结点
int data;
struct ArcNode *firstarc;
}VNode;
typedef struct{
VNode adjlist[maxsize]; //领接表
int vexnum, arcnum;
}AGraph;
void Dijstra_MIN(AGraph *G, int v, int A[maxnum][maxnum]){
int i,j,k,min,temp;
ArcNode *p = G->adjlist[v].firstarc;
int dist[G->vexnum] = {maxsize}; //这里不需要path数组,不需要求最短路径
int visited[G->vexnum] = {0};
while(p!=NULL){ //先将源点所连接的边加入最短路径当中
temp = p->adjvex;
dist[temp] = p->info;
p = p->next;
}
visited[v] = 1;
for(i = 0; i < G->vexnum-1; i++){
min = maxnum;
for(j = 0; j < G->vexnum; j++){
if(visited[j]==0 && dist[j] < min){
min = dist[j];
k = j;
}
}
visited[k] = 1;
p = G->adjlist[k].firstarc;
while(p!=NULL){
temp = p->adjvex;
if(visited[temp] == 0 && dist[k]+p->info < dist[temp]){
dist[temp] = dist[k]+p->info;
}
p = p->next;
}
}
for(i = 0; i < G->vexnum; i++){
A[i][v] = dist[i];
}
}
void FindCenter(AGraph *G){
int A[maxnum][maxnum];
int i,j,center,dist=0,mindist=maxnum;
for(i=0; i < G->vexnum; i++){
Dijstra_MIN(G,i,A);
}
for(i=0;i<G->vexnum; i++){
for(j=0; j < G->vexnum; j++){
dist = dist > A[j][i] ? dist : A[j][i];
}
if(dist < mindist){
mindist = dist;
center = i;
}
}
printf("图的中心为%d, 偏心距为%d\n",center, mindist);
}