图(C++)

本文详细介绍了邻接矩阵的初始化、插入边操作,以及邻接表的使用。还探讨了如何在带权图中实现结构,并举例了prim和kruskal最小生成树算法的实现,包括并查集和边权计算。
摘要由CSDN通过智能技术生成

1.邻接矩阵的初始化:

头文件:

#include<cstring>
​
memset(G,0,sizeof(G));

2.插入边:

无向图:

G[u] [v]=G[v] [u]=1;

有向图:

G[u] [v]=1

3.邻接矩阵

G[h] [l];
    memset(G,0,sizeof(G));
    int m;
    cin>>m;
    for(int i=0;i<m;i++){
        int a,b;
        cin>>a>>b;
        G[a] [b]=1;
    }

4.邻接表

形成邻接表:

#include<bits/stdc++.h>
using namespace std;
int main(){
    int max;
    cin>>max;
    vector<int> v;
    vector<int> G[max];
    int m;
    cin>>m;
    for(int i=0;i<m;i++){
        int a,b;
        cin>>a>>b;
        G[a].push_back(b);
        G[b].push_back(a);
    }
    for(int i=1;i<=10;i++){
        cout<<i<<":";
        for(int j=0;j<G[i].size();j++){
            cout<<G[i][j]<<" ";
        }
        cout<<endl;
    }
    return 0;
}

5.带权图

结构体定义:

struct Node{
    int v; //记录连接点
    int len; //记录边权
};

插入带权有向边:

vector<node> G[110];
    void insert1(int u,int v,int w){
        node tmp;
        tmp.v=v;
        tmp.len=w;
        G[u].push_back(tmp);
    }

插入无相边(=两条相反有向边)

void insert2(int u,int v,int w){
        insert1(u,v,w);
        insesrt1(v,u,w);
    }

用邻接表表示带权图:

输入:
void input(){
    int m;
    cin>>m;
    for(int i=0;i<m;i++){
        int u,v,w;
        cin>>u>>v>>w;
        insert2(u,v,w);
    }
}

输出:
void output(){
    for(int i=0;i<=10;i++){
        for(int j=0;j<G[i].size();j++){
            cout<<"("<<i<<","<<G[i][j].v<<","<<G[i][j].w<<")"<<endl;
        }
    }
}

6.最小生成树

prim算法

#include<bits/stdc++.h>
​
using namespace std;
const int N=5010,M=2e5+10;
const int INF=0x3f3f3f3f;
int n,m,cnt,ans,a[N] [N],dis[M];
bool vis[N];
void prim(){
    vis[1]=1;//从节点1开始
    for(int i=1;i<n;i++){
        int minn=INF;//查找当前最小点的边权
        int pos;//并记录该节点的下标
        for(int j=1;j<=n;j++){
            if(!vis[j] && (minn==INF || dis[j]<minn)){
                minn=dis[j];//记录最小点的边权
                pos=j;
            }
        }
        vis[pos]=1;
        ans+=minn;//累加答案,因为该点已经为最小生成数的边权
        ++cnt;
        if(minn==INF) break;
        //提前break,说明此时cnt一定不超过n,以此判断能否生成
        //此时minn=INF,说明没有比1e9更小的边权,但当前还在循环,即还有点未遍历是否未最小生成数的边权,产生矛盾
        for(int j=1;j<=n;j++){
            //更新当前最小生成树到其他点的最小距离
            //即:如果这点没有被访问过,而且这个点到任意一点的距离比现在到树的距离近那么更近
            if(!vis[j] && dis[j]>a[pos] [j]){
                dis[j]=a[pos] [j];//更新边权,即就将更小值放入dis数组
            }
        }
    }
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)初始化a,除了自己到自己的点为0,其余为边界值
        for(int j=1;j<=n;j++)
            if(i==j) a[i][j]=0;
            else a[i][j]=INF;
    for(int i=1;i<=m;i++){
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        a[x] [y]=a[y] [x]=min(a[x] [y],z);//避免重边
    }
    for(int i=1;i<=n;i++) dis[i]=a[1] [i];初始化距离数组,默认从顶点1开始计算,并把1能够到达的点放进dis数组
    prim();
    if(cnt>=n-1) printf("%d\n",ans);//能生成
    else puts("orz");
    return 0;
}

kruskal算法

#include<bits/stdc++.h>
​
using namespace std;
const int N=5100,M=2e5+50;
struct rec{
    int x,y,z;  
}edge[M];
int n,m,ans,cnt,fa[N];//cnt用来记录当前最小生成树的边的个数(避免改图不能生成树)
bool flag;//判断能否生成树
bool operator<(rec a,rec b){
    return a.z<b.z;//按边权从小到大排序(因为是最小生成树)
}
int get(int x){
    if(fa[x]==x) return x;
    return fa[x]=get(fa[x]);//不懂的看并查集文章~~~~
}
void Kruskal(){
    sort(edge+1,edge+1+m);
    for(int i=1;i<=n;i++) fa[i]=i;//并查集初始化
    for(int i=1;i<=m;i++){
        //寻找一条连接x与y的边,由于之前预处理了(sort),所以取的一定为当前最小边权
        int x_fa=get(edge[i].x);//查找x的祖先
        int y_fa=get(edge[i].y);//查找y的祖先
        if(x_fa==y_fa) continue;//若祖先一样说明已经属于同一集合,则跳过,即已经有可能成为最小生成树的一个
        fa[x_fa]=y_fa;//把x的祖先标记为y的祖先,即合并两个集合
        cnt++;//最小生成树的边的个树+1
        ans+=edge[i].z;//累加答案
        if(cnt==n-1) break;//若边数等于节点数-1说明,因为最小生成树边数等于点数-1
​
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&edge[i].x,&edge[i].y,&edge[i].z);//由于需要对边权排序,所以用树组直接存图
    }
    Kruskal();//Kruskal求最小生成树的全值和
    if(cnt==n-1) printf("%d",ans);//若存在最小生成数
    else puts("orz");//不存在,输出orz
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值