数据结构代码汇总(6)----终章---图论

本文将介绍图论相关算法,难度较大,如果有写的不对的地方欢迎评论区探讨
本文只讲图论算法,基础知识略过不讲
由于图论算法书上的代码可读性以及可执行性不高,故本文会将这些算法一一实现,长度会比较长,这是最伟大的一集(^ _ ^)这些算法仅使用一个测试点进行测试,如果大家写的时候遇到了错误,请反馈给我
一、图的存储与创建(书上的有点啰嗦,不简洁,不适宜考试中写)
1.邻接矩阵

typedef struct
{
    int mat[50][50];//矩阵
    int vexnum,arcnum;//顶点数,边数
}graph;//图的定义,这个存储方式比书上简化多了,考试大多是int类型,所以此定义是对于考试来说非常方便的

2.邻接表

typedef struct arcnode
{
    int num;
    struct arcnode *next;
}arcnode;//中间点

typedef struct vernode
{
    int data;
    arcnode *firstarc;
}vernode;//顶点

typedef struct
{
    vernode ver[50];
    int vexnum,arcnum;//顶点数,边数
}graph;

3.邻接矩阵的创建,课本217页算法7.1
有两种方法,一种是直接输入一个矩阵,此种方法非常简单,按照输入一般的矩阵就可以输入进去了
第二种是输入两个顶点及边的权值,如果这种方法,也不是很难

void creategraph(graph &g)//此处是c++写法,传的引用
{
    int i,j;
    scanf("%d %d",&g.vexnum,&g.arcnum);
    for(i=1;i<=g.vexnum;i++)
    {
        g.ver[i]=i;
        for(j=1;j<=g.vexnum;j++)
        {
            g.mat[i][j]=(i==j)?0:32767;//初始化,同一点则赋0,不是一个点则赋32767(一个特别大的数,表示不可达,32767是int类型上限,书上是INFINTY)
        }
    }

    int num1,num2,weigh;
    for(i=1;i<=g.arcnum;i++)
    {
        scanf("%d %d %d",&num1,&num2,&weigh);
        g.graph[num1][num2]=weigh;
        g.graph[num2][num1]=weigh;//有向网就没有这句了
    }
}

4.邻接表的创建(此算法课本好像没有?)

//需要先输入n(顶点的个数)
for(i=1;i<=n;i++)
    {
        scanf("%d",&num);
        g.ver[i].data=num;
        visited[i]=0;//全局变量,方便后面做遍历
        g.ver[i].firstarc=NULL;//初始化,千万要做
    }

    while(1)
    {
        scanf("%d %d",&a,&b);
        if(a==-1&&b==-1)break;//结束条件
        arcnode *p=(arcnode*)malloc(sizeof(arcnode));
        p->num=b;
        p->next=g.ver[a].firstarc;//头插法建立
        g.ver[a].firstarc=p;
    }

十字链表存储方法考试不考,此处不讲
二、图的遍历(225页的算法不讲,因为它只是思路,实现不了)
1.深度优先(递归,课本226页7.5-7.6)
1.1邻接矩阵实现的

void dfs(graph g,int v0)
{
    printf("%d ",v0);
    visited[v0]=1;//全局数组
    int i;
    for(i=0;i<g.vexnum;i++)
    {
        if(!visited[i] && g.mat[v0][i]==1)
            dfs(g,i);
    }
}

1.2邻接表实现的

void DFS(graph *g,int n)
{
    arcnode *p;
    int i;
    visited[n]=1;
    printf("%d ",g->ver[n].data);
    p=g->ver[n].firstarc;
    while(p)
    {
        i=p->num;
        if(!visited[i])
            DFS(g,i);//递归
        p=p->next;//按深度遍历就是一条路走到黑~
    }
}

1.3非递归形式的DFS,226页2.7

void push(stack *s,int n)//栈的操作
{
    s->a[++(s->top)]=n;
}

int pop(stack *s)
{
    int n=s->a[(s->top)--];
    return n;
}

int visited[50];

void DFS(graph g,int v0)
{
    stack s;
    s.top=-1;//栈的初始化,对应InitStack函数
    push(&s,v0);
    int v,w;
    arcnode *temp;//
    while(s.top!=-1)
    {
        v=pop(&s);
        if(!visited[v])
        {
            cout<<v<<" ";
            visited[v]=1;
            temp=g.ver[v].firstarc;
            if(temp)//判断邻接顶点是否为空,是则求出
                w=g.ver[v].firstarc->num;
            else
                w=-1;//否则赋值-1
            while(w!=-1)
            {
                if(!visited[w])
                    push(&s,w);
                if(temp->next)
                    w=temp->next->num;//同上
                else
                    w=-1;
                temp=temp->next;
            }
        }
    }
}

2.广度优先遍历(228页7.8)

typedef struct
{
    int arr[50];
    int front;
    int rear;
}seqquene;

void init(seqquene *a)
{
    a->front=a->rear=0;
}

void enter(seqquene *q,int x)
{
    q->arr[q->rear]=x;
    q->rear=(q->rear+1)%50;
}

int del(seqquene *q)
{
    int x=q->arr[q->front];
    q->front=(q->front+1)%50;
    return x;
}

int visited[50];

void BFS(graph g,int v0)
{
    cout<<v0<<" ";
    visited[v0]=1;
    seqquene q;
    init(&q);//队列的初始化,对应InitStack函数
    enter(&q,v0);
    int v,w;
    arcnode *temp;//
    while(q.rear!=q.front)
    {
        v=del(&q);
        temp=g.ver[v].firstarc;
        if(temp)//判断邻接顶点是否为空,是则求出
            w=g.ver[v].firstarc->num;
        else
            w=-1;//否则赋值-1
        while(w!=-1)
        {
            if(!visited[w])
            {
                cout<<w<<" ";
                visited[w]=1;
                enter(&q,w);
            }
            if(temp->next)
                w=temp->next->num;//同上
            else
                w=-1;
            temp=temp->next;
        }
    }
}

三、图的应用
1.简单路径,231页7.9

int pre[50]={-1};

void printpath(int n)
{
    int path[50];//存储路径的数组
    int pathIndex = 0;//路径数组的索引

    //从目标顶点开始,逆向追踪到起始顶点
    for(int current=n;current!=-1;current=pre[current])
    {
        path[pathIndex++]=current;//将顶点添加到路径数组中
    }

    //打印路径,路径是逆序的,所以需要反转打印
    for(int i=pathIndex-1;i>=0;i--)
    {
        cout<<path[i]<<" ";
    }
    cout<<endl;
}

void DFS(graph *g,int u,int v)
{
    int w;
    if(u==v)
    {
        printpath(v);
        return;
    }
    arcnode *p=g->ver[u].firstarc;
    if(p)
        w=g->ver[v].firstarc->num;
    else
        w=-1;
    while(w!=-1)
    {
        if(pre[w]==-1)
        {
            pre[w]=u;
            DFS(g,w,v);
        }
        if(p->next)
            w=p->next->num;
        else
            w=-1;
        p=p->next;
    }
}

注:没有写主函数的邻接表实现的操作,主函数均为以下的

int main()
{
    graph g;
    int i,a,b,num,n;
    cin>>n;
    for(i=1;i<=n;i++)
    {
        scanf("%d",&num);
        g.ver[i].data=num;
        pre[i]=-1;//全局变量,方便后面做遍历
        g.ver[i].firstarc=NULL;//初始化,千万要做
    }

    while(1)
    {
        scanf("%d %d",&a,&b);
        if(a==0&&b==0)break;//结束条件
        arcnode *p=(arcnode*)malloc(sizeof(arcnode));
        p->num=b;
        p->next=g.ver[a].firstarc;//头插法建立
        g.ver[a].firstarc=p;
    }
    DFS(&g,1,4);
    return 0;
}

2.最小生成树,prim算法,233页算法7.10

#include <iostream>

using namespace std;

typedef struct
{
    int ver[100];
    int graph[100][100];
    int vexnum,arcnum;
}Graph;//

typedef struct
{
    int adjvex;
    int low;
}closedge;

int all=0;//最小生成树的权值之和

void creategraph(Graph &g)//生成图的算法,前面讲过了
{
    int i,j;
    scanf("%d %d",&g.vexnum,&g.arcnum);
    for(i=1;i<=g.vexnum;i++)
    {
        g.ver[i]=i;
        for(j=1;j<=g.vexnum;j++)
        {
            g.graph[i][j]=(i==j)?0:32767;
        }
    }

    int num1,num2,weigh;
    for(i=1;i<=g.arcnum;i++)
    {
        scanf("%d %d %d",&num1,&num2,&weigh);
        g.graph[num1][num2]=weigh;
        g.graph[num2][num1]=weigh;
    }
}

int mini(Graph &g,closedge *s)
{
    int i,min,loc=-1;
    min=32767;
    for(i=1;i<=g.vexnum;i++)
    {
        if(min>s[i].low && s[i].low!=0)//选最小边
        {
            min=s[i].low;
            loc=i;
        }
    }
    return loc;
}

void prim(Graph &g,int n)
{
    int i,j,k;
    closedge s[100];
    s[1].low=0;

    for(i=2;i<=g.vexnum;i++)//初始化
    {
        s[i].adjvex=n;
        s[i].low=g.graph[n][i];
    }
    cout<<"1";
    for(k=1;k<g.vexnum;k++)
    {
        j=mini(g,s);//选取当前最小边
        all+=s[j].low;//最小边权值加入到最小生成树的总权值中
        printf(" %d",j);//输出边的信息
        s[j].low=0;//
        for(i=1;i<=g.vexnum;i++)
        {
            if(g.graph[j][i]<s[i].low)//更新边的信息
            {
                s[i].low=g.graph[j][i];
                s[i].adjvex=j;
            }
        }
    }
}

int main()
{
    Graph g;
    creategraph(g);
    prim(g,1);
    cout<<endl<<all;
    return 0;
}

3.拓扑排序,239页7.11 7.12

#include <iostream>
#include <stack>
using namespace std;

int a[50];
int n=0;
typedef struct arcnode
{
    int adjvex;
    struct arcnode *next;
}arcnode;

typedef struct vernode
{
    int data;
    arcnode *first;
}vernode;

typedef struct
{
    vernode ver[50];
    int vexnum,arcnum;
}graph;


void findid(graph &g,int indegree[])//求入度
{
    int i;
    arcnode *p;
    for(i=0;i<g.vexnum;i++)//初始化入度数组
        indegree[i]=0;
    for(i=0;i<g.vexnum;i++)
    {
        p=g.ver[i].first;//求每个顶点的入度
        while(p)
        {
            indegree[p->adjvex]++;//p指向的点入度+1,不停循环
            p=p->next;
        }
    }
}

void topsort(graph &g)
{
    stack<int> s;//此处偷懒了,调用stl,如果考试的时候不让用请使用前面栈操作来替换
    int indegree[50];
    int i,cnt,k;
    arcnode *p;
    findid(g,indegree);//求入度
    for(i=0;i<g.vexnum;i++)
    {
        if(!indegree[i])//入度为0则进栈
            s.push(i);
    }
    cnt=0;
    while(!s.empty())
    {
        i=s.top();
        a[n++]=i;//先存到数组中,一会一起输出
        s.pop();//出栈
        cnt++;
        p=g.ver[i].first;
        while(p)
        {
            k=p->adjvex;
            indegree[k]--;//删掉节点对应的边
            if(!indegree[k])//入度为0就进栈
                s.push(k);
            p=p->next;
        }
    }
    if(cnt < g.vexnum)
        cout<<"ERROR";
    else
    {
        for(i=0;i<n;i++)
            cout<<a[i]<<" ";
    }
}

int main()
{
    graph g;
    int i,j,n;
    arcnode *p,*q;
    cin>>g.vexnum;
    for(i=0;i<g.vexnum;i++)//初始化
    {
        g.ver[i].first=NULL;
        g.ver[i].data=i;
    }
    //此处是输入的邻接矩阵,但存储方式使用邻接表
    for(i=0;i<g.vexnum;i++)//尾插法创建图
    {
        for(j=0;j<g.vexnum;j++)
        {
            cin>>n;
            if(n && g.ver[i].first==NULL)
            {
                p=new arcnode;
                p->adjvex=j;
                g.ver[i].first=p;
                p->next=NULL;
            }
            else if(n)
            {
                q=g.ver[i].first;
                while(q->next)//尾插法,找到最后
                    q=q->next;
                p=new arcnode;
                q->next=p;
                p->next=NULL;
                p->adjvex=j;
            }
        }
    }
    topsort(g);
    return 0;
}

关键路径不考此处略去

4.迪杰斯特拉最短路径,248页7.15,全书最想吐槽的算法,此算法书上的可读性极差(个人感觉),addtail算法根本不给,path的定义也不明确。。。

#include <iostream>

using namespace std;

typedef struct
{
    int mat[50][50];//矩阵
    int vexnum,arcnum;//顶点数,边数
}graph;

int dist[50],path[50][50];//dist是最短路径的长度,path是最短路径的路径,其实这两个就是个整型数组,不要被书上的定义吓到了

int member(int n,int s[])
{
    int i;
    for(i=0;i<50;i++)
    {
        if(s[i]==n)
            return 1;
    }
    return 0;
}

void addtail(int path[][50],int v0,int v)
{
    int i=0;
    for(i=0;path[v0][i]!=-1;i++);//注意这里的分号,别抄错了
    path[v0][i]=v;
}

void dij(graph g,int v0)//其余参数以全局变量的形式带入
{
    int s[50];//最短路径点的集合,其实也是个整型数组
    int i,j,k,min;
    int scount=0;//控制s集合的计数器
    for(i=0;i<g.vexnum;i++)
    {
        //path数组在主函数已经初始化过了
        dist[i]=g.mat[v0][i];
        if(dist[i]<32767)
        {
            path[i][0]=v0;
            addtail(path,i,i);
        }
    }
    for(i=0;i<50;i++)//
    {
        s[i]=-1;
    }

    s[scount++]=v0;//纳入s集合,表示已经访问过了,类似visited数组

    for(i=0;i<g.vexnum;i++)
    {
        min=32767;
        for(j=0;j<g.vexnum;j++)
        {
            if(!member(j,s) && dist[j]<min)
            {
                k=j;
                min=dist[j];
            }
        }
        if(min==32767)return;

        s[scount++]=k;

        for(j=0;j<g.vexnum;j++)
        {
            if(!member(j,s) && g.mat[k][j]!=32767 && (dist[k]+g.mat[k][j]<dist[j]))//权值小就改变,类似prim算法
            {
                dist[j]=dist[k]+g.mat[k][j];
                for(int m=0;path[k][m]!=-1;m++)
                {
                    addtail(path,j,path[k][m]);//把对应的节点路径上都添加此节点
                }
                addtail(path,j,k);
            }
        }
    }
}

int main()
{
    graph g;
    int i,j,v0;//v0是源点
    cin>>g.vexnum;
    cin>>v0;

    for(i=0;i<g.vexnum;i++)
    {
        for(j=0;j<g.vexnum;j++)
        {
            cin>>g.mat[i][j];
            if(g.mat[i][j]==0)
                g.mat[i][j]=32767;//初始化为不可达
        }
    }

    for(i=0;i<50;i++)
    {
        for(j=0;j<50;j++)
            path[i][j]=-1;//初始化
    }
    dij(g,v0);
    for(i=0;i<g.vexnum;i++)
    {
        if(dist[i]==32767)
        {
            cout<<"Vertex "<<i<<" is unreachable from "<<v0<<endl;
        }
        else
        {
            cout<<"Shortest distance from "<<v0<<" to "<<i<<" is "<<dist[i]<<endl;
            cout<<"Path: "<<v0;
            for(int step=0;path[i][step]!=-1;step++)
            {
                cout<<" -> "<<path[i][step];//输出路径
            }
            cout<<endl;
        }
    }
    return 0;
}

5.弗洛伊德最短路径,251页7.16
下面的代码是基于这道题实现的
在这里插入图片描述

#include <iostream>

using namespace std;

typedef struct
{
    int vexnum,edgenum;
    int matrix[50][50];
}graph;

int dist[50][50];
int path[50][50];
void flyd(graph g)
{
    int i,j,k;

    for(i=0; i<g.vexnum; i++)
    {
        for(j=0; j<g.vexnum; j++)
        {
            dist[i][j]=g.matrix[i][j];//初始化
            if(i != j && path[i][j] != 9999)
                path[i][j]=j;
        }
        path[i][i]=0;
        dist[i][i]=0;
    }

    for(k=0; k<g.vexnum; k++)
    {
        for(i=0; i<g.vexnum; i++)
        {
            for(j=0; j<g.vexnum; j++)
            {
                if(dist[i][k] == 9999 || dist[k][j] == 9999)//这句话是为了减少后面判断的
                    continue;
                if(dist[i][j] > (dist[i][k]+dist[k][j]))
                {
                    dist[i][j]=dist[i][k]+dist[k][j];
                    path[i][j]=path[i][k];//更新路径
                }
            }
        }
    }
    for(i=0;i<g.vexnum;i++)
    {
        for(j=0;j<g.vexnum;j++)
        {
            cout<<"from "<<i<<" to "<<j<<": dist = "<<dist[i][j];
            k=i;
            cout<<" path:";
            while(k!=j)
            {
                cout<<k<<" ";
                k=path[k][j];
            }
            cout<<j<<" "<<endl;
        }
    }

}
int main()
{
    int i,j;
    graph g;
    cin>>g.vexnum;
    for(i=0;i<g.vexnum;i++)
    {
        for(j=0;j<g.vexnum;j++)
        {
            cin>>g.matrix[i][j];
        }
    }
    flyd(g);
    return 0;
}

本系列文章到此结束,由于查找和排序书上的代码可抄性非常高,我就不再翻译了,大家对算法有什么不理解的地方欢迎评论,实在不理解的就请先记下来,因为我写的这些代码真的能跑。
图论这一篇文章是最难写的,因为好多操作书上没有给,不管怎么说,我还是总结了一遍,希望对大家的期末复习有所帮助,感谢大家的阅读。

  • 8
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值