【数据结构】图

1.存储结构

1.1邻接矩阵

//邻接矩阵 
typedef struct{
	int no;
	char data;
}Vertex;//顶点 

typedef struct{
	int edge[MAXN][MAXN];
	int n,e;//顶点数,边数 
	Vertex vex[MAXN];
}MGraph;

1.2邻接表

//邻接表
typedef struct{
	int adjVex;//指向节点序号	
	struct ArcNode * nextarc;
}ArcNode;//边节点 

typedef struct{
	char data;
	ArcNode * firstArc;
}VNode;//顶点节点 
 
typedef struct{
	VNode adjList[MAXN];
	int n,e;//顶点数,边数 
}AGraph;

1.2.1数组实现

/*
若需节省空间,可用vector进行优化
*/
#include<bits/stdc++.h>
using namespace std;
#define N 6
int u[N],v[N],w[N];
int first[N],next0[N];

//next数组使用时可能导致reference to ‘next’ is ambiguous
//为了兼容,不使用max,next等关键字
//顶点序号从1开始,故数组设置时需大于顶点数+1
int main()
{
       int n,m;//顶点数,边数
       //初始化first数组
       cin>>n>>m;
       for(int i=1;i<=n;i++)
           first[i]=-1;//结尾标记
       //读入边
       for(int i=1;i<=m;i++)
       {
           cin>>u[i]>>v[i]>>w[i];
           //类似于头插法,可将first理解为每一个节点的存储“链表”的"头节点"
           //u[i],是读入顶点的编号
           next0[i]=first[u[i]];
           first[u[i]]=i;
       }
       //遍历
       for(int i=1;i<=n;i++)
       {
           int k=first[i];
           while(k!=-1)
           {
               cout<<u[k]<<"\t"<<v[k]<<"\t"<<w[k]<<endl;
               k=next0[k];
           }
        }
   return 0;
}

1.2.2链表实现

#include<bits/stdc++.h>
using namespace std;
#define N 20
//嵌套结构体的不完全声明
typedef struct vertex Vertex;
typedef struct edge Edge;
typedef struct graph Graph;

struct vertex//顶点
{
    int num;//顶点编号
    Edge * first;
};

struct edge//边
{
    int num;//编号
    //int weight 可增加权值
    Edge *next;
};

struct graph
{
    Vertex vlist[N];//顶点表
    int n,m;//顶点数,边数
};

int book[N];//遍历标记
Graph G;
void DFS(int i)
{
    book[i]=1;
    cout<<G.vlist[i].num<<"\t";
    for(Edge *p=G.vlist[i].first;p!=NULL;p=p->next)
        if(!book[p->num])DFS(p->num);
}


int main()
{

    cout<<"input:vertex edge"<<endl;
    cin>>G.n>>G.m;
    for(int i=1;i<=G.n;i++)//建立顶点表,编号均从1开始
    {
         G.vlist[i].num=i;
         G.vlist[i].first=NULL;
    }

    //读入边信息
    cout<<"input edge:(弧尾,弧头)"<<endl;
    for(int i=1;i<=G.m;i++)
    {
        int tail,head;//弧尾,弧头
        cin>>tail>>head;
        //邻接表
        Edge *s1= new Edge;//边
        s1->num=head;
        /*
        头插法会导致读入顺序与输入顺序相反,
        但邻接表同一顶点的出度出现的顺序无关紧要
        */
        s1->next=G.vlist[tail].first;
        G.vlist[tail].first=s1;
    }
    //遍历
    cout<<"**********邻接表*************"<<endl;
    for(int i=1;i<=G.n;i++)
    {
        Edge * p=G.vlist[i].first;
        cout<<G.vlist[i].num;
        while(p!=NULL){
                cout<<"---->"<<p->num;
                p=p->next;
        }
        cout<<endl;
    }
    /*
    由于邻接表记录的是出度,统计度的时候需要一个个
    去遍历不方便,可增加逆邻接表提高效率
    逆邻接表反应的是入度的情况
    */
    DFS(1);//从1号节点开始遍历
    return 0;
}

2.遍历算法

2.1深度优先遍历

#include<bits/stdc++.h>
using namespace std;

int n,m,c,d,sum;
int book[100],a[100][100];

void dfs(int cur)//编号
{
    cout<<cur<<"\t";
    sum++;
    if(sum==n)return ;
    for(int i=1;i<=n;i++)//尝试编号
    {
        if(a[cur][i]==1&&book[i]==0)
            {
                book[i]=1;
                dfs(i);
            }
    }
    return ;
}

int main()
{

    cin>>n>>m;//顶点数,边数
    //初始化邻接矩阵
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            if(i==j)a[i][j]==0;
            else a[i][j]=9999;

    for(int i=0;i<m;i++){
         cin>>c>>d;
         a[c][d]=1;
         a[d][c]=1;
    }
    book[1]=1;
    dfs(1);
    return 0;
}

2.2广度优先遍历

#include<bits/stdc++.h>
using namespace std;

int n,m,c,d,sum;
int book[100],a[100][100];
queue <int> q;

void bfs(int cur)//编号
{
    q.push(cur);
    book[cur]=1;
    sum=0;
    while(!q.empty())
    {
        int t=q.front();
        cout<<t<<"\t";
        q.pop();
        for(int i=1;i<=n;i++)
        {
            if(a[t][i]==1&&book[i]==0)
            {
                book[i]=1;
                q.push(i);
            }
        }
        sum++;
        if(sum>=n)break;
    }
}

int main()
{

    cin>>n>>m;//顶点数,边数
    //初始化邻接矩阵
    for(int i=1;i<=n;i++)//从1开始输入
        for(int j=1;j<=n;j++)
            if(i==j)a[i][j]==0;
            else a[i][j]=9999;

    for(int i=0;i<m;i++){
         cin>>c>>d;
         a[c][d]=1;
         a[d][c]=1;
    }
    bfs(1);
    return 0;
}

3.最短路径

3.1多源最短路径

3.1.1Floyd-Warshall

#include<bits/stdc++.h>
using namespace std;
#define N 100

int a[N][N];
int n,m,c,d,t;

void Floyd_Warshall()//多源最短路径求解
{
    for(int k=1;k<=n;k++){
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                if(a[i][j]>a[i][k]+a[k][j])
                    a[i][j]=a[i][k]+a[k][j];
            }
        }
    }
}

void out()
{
     for(int i=1;i<=n;i++){
            cout<<"\n";
          for(int j=1;j<=n;j++){
             cout<<a[i][j]<<"\t";
        }
    }
}

int main()
{
    cin>>n>>m;//vertex,edge
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            if(i==j)a[i][j]=0;
            else a[i][j]=99999;
    out();//初始化后输出测试
    for(int i=1;i<=m;i++){
        cin>>c>>d>>t;
        a[c][d]=t;
    }
    Floyd_Warshall();
    out();
}

3.2单源最短路径

3.1.1Dijkstra

* 该算法要解决的问题,找到某顶点到其它顶点的最短距离

* 不能解决带负边权的情况

* 原理:对于顶点A最短邻接边B,从A到B不可能找到比AB更短的路径,基于贪心策略

* 时间复杂度:O(n^2)、堆优化后O(logn)
算法分为四步骤
1.
已知最短距离的集合P,未知最短距离的集合Q
先把源点加入到集合P中,book数组用来标记在哪个集合中,
book[i]=0,表示源点到i点的最短距离未知,book[i]=1表示已知
2.
设置dis数组,对于能直接走到的,设置dis[i]=e[源点][i],不能
直接到达的则设置为INF
3.优化(广搜思想)
将dis数组里面离源点距离最近的点u拿出来扩展,实现边松弛
4.重复第三步操作直至集合Q为空,算法结束
/*未用堆优化的实现*/
#include<bits/stdc++.h>
using namespace std;
#define N 20
#define inf 999999

int e[N][N],dis[N],book[N];
int n,m;//顶点数,边数

void Dijkstra()
{
    int min,u;
    /*为什么循环n-1遍,因为对于最后一个顶点来说,必然是
    距离源点最远的,不可能再通过该最远的点实现松弛操作*/
    for(int i=0;i<n-1;i++)
    {
        min=inf;
        /*找距离源点最近的点*/
        for(int j=1;j<=n;j++){//遍历整个dis数组找到距离源点最近的点且需在Q集合中
            if(dis[j]<min&&book[j]==0){
                min=dis[j];
                u=j;//找到该点记录编号
            }
        }
        /*找距离源点最近的点*/
        book[u]=1;//将该点加入集合P并进行考察
        /*实现边松弛*/
        for(int v=1;v<=n;v++){
            if(e[u][v]<inf){//能通过u中转到v,即考察每一条边
                if(dis[u]+e[u][v]<dis[v])
                    dis[v]=dis[u]+e[u][v];
            }
        }
        /*实现边松弛*/
    }

}

int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++)
            if(i==j)e[i][j]=0;
        else e[i][j]=inf;
    }
    //读入边
    for(int i=1;i<=m;i++){
        int a,b,c;
        cin>>a>>b>>c;
        e[a][b]=c;//有向图
    }
    //这里设置的源点为1
    for(int i=1;i<=n;i++)//初始化1-各点距离
        dis[i]=e[1][i];
    book[1]=1;
    Dijkstra();
    //输出
    for(int i=1;i<=n;i++)
        cout<<dis[i]<<"\t";

    return 0;
}


4.最小生成树

4.1Prim

4.2Kruskal

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值