图实验(基于邻接表)--合工大数据结构实验6+7

图实验(基于邻接表)–合工大数据结构实验6+7

graph_Node.h

#include"createGrpAdjLinkedList.h"//邻接表
#include<queue>
#include<algorithm>
#define INTF 2147483646//定义int最大值
void shownemu()
{
    cout<<"-------------------------------------------菜单---------------------------------------\n";
    cout<<"                             -------------实验六-------------\n";
    cout<<"1-打印出图(网)的两种遍历序\n";
    cout<<"2求给定图中的边(或弧)的数目\n";
    cout<<"3对给定的图G及出发点v0,从V0出发深度优先遍历图G,并构造出相应的生成树或生成森林\n";
    cout<<"4对给定的图G及出发点v0,从V0出发广度优先遍历图G,并构造出相应的生成树或生成森林\n";
    cout<<"5实现Prim算法,求解给定图G的最小生成树\n";
    cout<<"6实现Kruskal算法,求解给定图G的最小生成树\n";
    cout<<"                             -------------实验七-------------\n";
    cout<<"7实现Dijkstra算法,求解给定图G指定顶点到其余顶点之间的最短路径\n";
    cout<<"8实现Floyd   算法,求解给定图G各顶点之间的最短路径\n";
    cout<<"9求解给定图G的拓扑序列\n";
    cout<<"10求解给定AOE网的关键路径\n";
    cout<<"   说明:此文件以邻接表为存储结构\n";
    cout<<"--------------------------------------------------------------------------------------\n";
}
/*第一题*/
// DFS递归函数
void DFS(Graph2 graph, int v, bool visitedd[]) {
    visitedd[v] = 1;  // 标记当前顶点为已访问
    cout<<graph.VerList[v].data<<" ";

    EdgeNode* edge = graph.VerList[v].firstEdge;//指向第一个邻接点
    while (edge != NULL) {
        if (visitedd[edge->adjVer] == 0) {
            DFS(graph, edge->adjVer, visitedd);
        }
        edge = edge->next;
    }
}
/*dfs遍历函数*/
void DFSTraverse(Graph2 graph,int v) {
    bool visitedd[MaxVerNum + 1] = { 0 };  // 访问标记数组,初始都为0
    DFS(graph,v,visitedd);//从v开始
    for (int v = 1; v <= graph.VerNum; v++) {
        if (visitedd[v] == 0) {
            DFS(graph, v, visitedd);//查找非连通图中剩下的
        }
    }
}
//BFS函数
void BFS(Graph2 graph, int v, bool visitedd[]){
    visitedd[v]=1;//标记访问
    queue<int> q;
    q.push(v);//将首个元素入队
    while(!q.empty())//队列不为空
    {
        int temp=q.front();
        q.pop();
        cout<<graph.VerList[temp].data<<" ";//输出队头元素
        EdgeNode *p=graph.VerList[temp].firstEdge;
        while(p)//当p不为空 也就是temp下标结点存在临界点
        {
            if(!visitedd[p->adjVer])
                {
                    q.push(p->adjVer);
                    visitedd[p->adjVer]=1;
                }//将p的下标入队
            p=p->next;
        }
    }
}
/*bfs遍历函数*/
void BFSTraverse(Graph2 graph ,int v){
    bool visitedd[MaxVerNum + 1] = { 0 };  // 访问标记数组,初始都为0
    BFS(graph,v,visitedd);//BFS从v开始
    for (int v = 1; v <= graph.VerNum; v++) {
        if (visitedd[v] == 0) {
            BFS(graph, v, visitedd);//遍历非连通图中剩余的
        }
    }
}
/*第二题*/
int vernum(Graph2 g)//输出边或弧的数目  尽管结构体里封装了边的数目 但为了程序完整性 自己写了一遍
{
    int n=g.VerNum;
    int num=0;
    EdgeNode *p=NULL;
    for(int i=1;i<=n;i++)
    {
        p=g.VerList[i].firstEdge;
        while(p!=NULL) //若p不为空 说明顶点1存在邻接点 也就是存在一条边
        {
            num++;
            // cout<<g.VerList[i].data<<"->"<<g.VerList[p->adjVer].data<<" ";//输出当前结点元素和下一个节点元素
            p=p->next;//p指向下一个邻接点
            // if(num%5==0) cout<<"\n";
        }
    }
    /*特判是否为无向图 因为无向图计算的时候算了两遍*/
    if(g.gKind==0||g.gKind==1) num/=2;
    return num;
}
/*第三题 构建dfs生成树*/

void dfs_generate_tree(Graph2 g, bool visitedd[], int u, int parent[]) {
    visitedd[u] = true;
    EdgeNode* p = g.VerList[u].firstEdge;
    while (p) {
        if (!visitedd[p->adjVer]) {
            parent[p->adjVer] = u;
            dfs_generate_tree(g, visitedd, p->adjVer, parent);
        }
        p = p->next;
    }
}

void dfsTraverse_3(Graph2 g, bool visitedd[], int u) {
    int parent[MaxVerNum + 1];
    for (int i = 1; i <= g.VerNum; i++) {
        
        parent[i] = -1;
    }
    dfs_generate_tree(g, visitedd, u, parent);
    /*打印生成树*/
    for (int i = 1; i <= g.VerNum; i++) {
        if (parent[i] != -1) {
            printf("%c->%c ", g.VerList[parent[i]].data, g.VerList[i].data);
        }
    }
    // 遍历其他连通分量
     for(int i = 1; i <= g.VerNum; i++)
    {
        if(!visitedd[i])
        {
            DFS(g, i,visitedd);
        }
    }
}
/*第四题 构建bfs生成树*/
void bfs_generate_tree(Graph2 g, bool visited[], int parent[], int v) {
    int queue[MaxVerNum];
    int front = 0;
    int rear = 0;

    // 从顶点 v 开始构建生成树
    queue[rear++] = v;
    visited[v] = true;

    while (front != rear) {
        int u = queue[front++];

        EdgeNode* p = g.VerList[u].firstEdge;
        while (p) {
            int w = p->adjVer;
            if (!visited[w]) {
                visited[w] = true;
                parent[w] = u;
                queue[rear++] = w;
            }
            p = p->next;
        }
    }
}

void bfsTraverse_4(Graph2 g, bool visitedd[],int v) {
    // bool visited[MaxVerNum + 1] = { false };
    int parent[MaxVerNum + 1];

    for (int i = 1; i <= g.VerNum; i++) {
        parent[i] = -1;
    }

    bfs_generate_tree(g, visitedd, parent, v);

    // 打印生成树
    printf("BFS生成树的边信息:\n");
    for (int i = 1; i <= g.VerNum; i++) {
        if (parent[i] != -1) {
            printf("(%c, %c)\n", g.VerList[parent[i]].data, g.VerList[i].data);
        }
    }
}
/*第五题 以下是prim算法*/
void Prim(Graph2 g)
{
    int lowCost[100];// 存储顶点到最小生成树的最小权值
    int closest[100];// 存储最小生成树中与当前顶点最近的顶点
    for(int i=1;i<=g.VerNum;i++)
    {
        lowCost[i]=INF;
        closest[i]=1;
    }
    EdgeNode*p=g.VerList[1].firstEdge;
    // lowCost[1]=INF;
    // int i=1;
    while(p)//更新上述两个数组 初始时将顶点1与其他顶点的权值加入到lowCost数组中
    {
        lowCost[p->adjVer]=p->eInfo;
        p=p->next;
    }
    for(int i=2;i<=g.VerNum;i++)
    {
        int minCost = INF;
        int minVertex = -1;
        /*在当前顶点集合中找到距离生成树最近的顶点*/
        for(int j=2;j<=g.VerNum;j++)
        {
            if(lowCost[j]!=0&&lowCost[j]<minCost)
            {
                minCost=lowCost[j];
                minVertex=j;
            }
        }
        if(minVertex==-1) {
            break;
        }
        cout<<g.VerList[closest[minVertex]].data<<"->"<<g.VerList[minVertex].data<<":"<<minCost<<endl;
        // 将最近的顶点加入最小生成树
        lowCost[minVertex] = 0;
         // 更新与最近顶点相连的顶点到最小生成树的最小权值
        EdgeNode* q = g.VerList[minVertex].firstEdge;
        while (q) {
            int adjVer = q->adjVer;
            int weight = q->eInfo;
            if (weight < lowCost[adjVer]) {
                lowCost[adjVer] = weight;
                closest[adjVer] = minVertex;
            }
            q = q->next;
        }
        
    }
    
}
/*以下是Kruskal算法*/
typedef struct {
    int start;//边起点
    int end;//边终点
    int weight;//边权值
} Edge;

bool compareEdges(const Edge& e1, const Edge& e2) {
    return e1.weight < e2.weight;
}//比较规则
//并查集 寻找根节点
int findRoot(vector<int>& p, int x) {
    if (p[x] !=x)
        p[x] = findRoot(p, p[x]);
    return p[x];
}
void Kruskal(Graph2 g)
{
    vector<Edge> edges;//用数组存储边
    for(int i=1;i<=g.VerNum;i++)//所有的边入数组
    {
        EdgeNode*p=g.VerList[i].firstEdge;
        while(p)
        {
            int start=i;
            int end=p->adjVer;
            int weight=p->eInfo;
            edges.push_back({start,end,weight});
            p=p->next;
        }
    }
    sort(edges.begin(),edges.end(),compareEdges);
    vector<int> parent(g.VerNum+1);
    for (int i=1;i<=g.VerNum;i++) {
        parent[i]=i;//初始化
    }
    vector<Edge> mst;//结果数组
    int numEdges=0;
        for (const auto&edge:edges) {
        int rootStart=findRoot(parent,edge.start);
        int rootEnd=findRoot(parent,edge.end);

        if (rootStart!=rootEnd) {
            mst.push_back(edge);
            numEdges++;
            parent[rootStart] = rootEnd;

            if (numEdges==g.VerNum-1)//当生成树的顶点数等于图的总结点数-1时跳出循环
                break;
        }
    }
    cout << "用Kauskal算法最小生成树为:" << endl;
    for (auto&edge:mst) {//输出结果
       cout<<g.VerList[edge.start].data<<"--"<<g.VerList[edge.end].data<<":"<<edge.weight<<endl;
    }
}
/*拓扑排序 */
bool topo(Graph2 g,vector<int>& topologicalOrder)
{
    vector<int> ans;
    int in[100];//记录入度数组
    for(int i=1;i<=g.VerNum;i++) in[i]=0;
    for(int i=1;i<=g.VerNum;i++)
    {
        EdgeNode*p=g.VerList[i].firstEdge;//指向i的第一个邻接点
        while(p)//存在邻接点
        {
            in[p->adjVer]++;//该下标入度加1
            p=p->next;
        }
    }
    queue<int>q;
    for(int i=1;i<=g.VerNum;i++)
    {
        if(in[i]==0) q.push(i);
    }
    while(!q.empty())
    {
        int current = q.front();
        ans.push_back(current);
        q.pop();
        EdgeNode*temp=g.VerList[current].firstEdge;
        while(temp)
        {
            in[temp->adjVer]--;
            if(in[temp->adjVer]==0)
            {
                q.push(temp->adjVer);
            }
            temp=temp->next;
        }
    }
    if(ans.size()!=g.VerNum) return false;
    topologicalOrder=ans;
    return true;
}
/*以下是关键路径求解*/
void CriticalPath(Graph2& graph)
{
    vector<int> topologicalOrder;                  // 拓扑排序的结果
    vector<int> ve(graph.VerNum + 1, 0);           // 事件的最早发生时间
    vector<int> vl(graph.VerNum + 1, INF);         // 事件的最晚发生时间

    if (!topo(graph, topologicalOrder)) // 通过拓扑排序判断是否存在环
    {
        cout << "存在环,无法求解关键路径" << endl;
        return;
    }

    // 计算事件的最早发生时间
    for (int i = 0; i < topologicalOrder.size(); i++)
    {
        int v = topologicalOrder[i];
        EdgeNode* p = graph.VerList[v].firstEdge;
        while (p)
        {
            int w = p->adjVer;
            ve[w] = max(ve[w], ve[v] + p->eInfo);
            p = p->next;
        }
    }

    // 初始化结束顶点的最晚发生时间为其最早发生时间
    vl[graph.VerNum] = ve[graph.VerNum];

    // 计算事件的最晚发生时间
    for (int i = topologicalOrder.size() - 1; i >= 0; i--)
    {
        int v = topologicalOrder[i];
        EdgeNode* p = graph.VerList[v].firstEdge;
        while (p)
        {
            int w = p->adjVer;
            vl[v] = min(vl[v], vl[w] - p->eInfo);
            p = p->next;
        }
    }

    // 计算关键路径
    cout << "关键路径:" << endl;
    for (int i = 1; i <= graph.VerNum; i++)
    {
        EdgeNode* p = graph.VerList[i].firstEdge;
        while (p)
        {
            int j = p->adjVer;
            int e = ve[i];
            int l = vl[j] - p->eInfo;
            if (e == l)
                cout << graph.VerList[i].data << " -> " << graph.VerList[j].data << endl;
            p = p->next;
        }
    }
}
/*Floyd算法*/
int path[100][100];
vector<vector<int>> Floyd(Graph2 g)
{
    int n=g.VerNum;
    vector<vector<int>> dist(100,vector<int>(100,0));
    /*初始化dist和path*/
    for(int i=1;i<=g.VerNum;i++)
    {
        for(int j=1;j<=g.VerNum;j++)
        {
            dist[i][j]=INF;
            path[i][j]=-1;
        }
    }
    for(int i=1;i<=g.VerNum;i++)
    {
        EdgeNode*p=g.VerList[i].firstEdge;//第一个邻接点
        while(p)
        {
            dist[i][p->adjVer]=p->eInfo;//初始化i到p->adjVer(下标)的距离
            path[i][p->adjVer]=i;
            p=p->next;
        }
    }
    for(int m=1;m<=n;m++)
    {
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                if(dist[i][j]>dist[i][m]+dist[m][j]&&i!=j)
                {
                    dist[i][j]=dist[i][m]+dist[m][j];
                    path[i][j]=path[m][j];
                }
            }
        }
    }
    return dist;
}
/*以下是Dijkstra算法*/
vector<int> Dijkstra(Graph2 g,int v0,bool visitedd[])//7 //此函数返回距离数组
{
    int miin,pos;
   visitedd[v0]=true;//返回指定顶点距离各顶点之间的距离
   int n=g.VerNum;
   vector<int> dist(100,INF);
   for(int i=1;i<=g.VerNum;i++) dist[i]=INF;
   /*初始化dist数组*/
    EdgeNode*p=g.VerList[v0].firstEdge;
    while(p)
    {
        dist[p->adjVer]=p->eInfo;
        p=p->next;
    }

   for(int i=1;i<=n;i++)
   {
        miin=INF;
        for(int j=1;j<=n;j++)
        {
            if(!visitedd[j]&&dist[j]<miin)
            {
                miin=dist[j];
                pos=j;
            }
        }
        /*此次循环之后找到了未在已选点中的点的集合中到起点的最短距离的那个点 下标为j 记录为pos*/
        /*然后更新dist数组*/
        visitedd[pos]=true;
                EdgeNode*temp=g.VerList[pos].firstEdge;
                while(temp)
                {
                    if(!visitedd[temp->adjVer]&&dist[temp->adjVer]>dist[pos]+temp->eInfo)
                    {
                        dist[temp->adjVer]=dist[pos]+temp->eInfo;
                    }
                    temp=temp->next;
                }
        
   }
   return dist;
}

experiment6_Node.cpp


#include"graph_Node.h"
typedef long long ll;
typedef long double ld;
const int mod=1e5+7;
using namespace std;
int main() {
    shownemu();
    int choice;
    cout<<"请输入你的选择";
    cin>>choice;
    while(choice)
    {
        /*基于邻接矩阵*/
        char filename[100];
        Graph2 g;
        cout<<"请输入文件名称:";
        cin>>filename;
        bool jls=CreateGraphFromFile(filename,g);
        if(jls) printGraph(g);
        switch(choice)
        {
            case 1:
            {
                cout<<"dfs遍历结果:";
                DFSTraverse(g,1);
                cout<<"\nBFS遍历结果:";
                BFSTraverse(g,1);
                system("pause");
                break;
            }
            case 2:
            {
                // cout<<"边或弧的个分别为\n";
                int n=vernum(g);
                cout<<"边或弧的个数为"<<n<<endl;
                system("pause");
                break;
            }
            case 3:
            {
                bool visitedd[100];
                for(int i=1;i<=g.VerNum;i++) visitedd[i] = false;
                cout<<"请输入出发点v0";
                int v0; cin>>v0;
                cout<<"从v0开始的dfs遍历为:";
                DFSTraverse(g,v0);
                cout<<endl;
                cout<<"\ndfs生成树为:";
                dfsTraverse_3(g,visitedd,v0);
                system("pause");
                break;
                
            }
            case 4:
            {
                bool visitedd[100];
                for(int i=1;i<=g.VerNum;i++) visitedd[i] = false;
                cout<<"请输入出发点v0";
                int v0; cin>>v0;
                cout<<"从v0开始的bfs遍历为:";
                BFSTraverse(g,v0);
                cout<<endl;
                cout<<"\nbfs生成树为:";
                bfsTraverse_4(g,visitedd,v0);
                system("pause");
                break;
                system("pause");
                break;
            }
            case 5:
            {
                Prim(g);
                system("pause");
                break;
            }

            case 6:
            {
                Kruskal(g);
                system("pause");
                break;
            }
            case 7:
            {
                cout<<"Dijkstra算法求最短路径\n";
                int begin;
                cout<<"请输入起点位置下标:";cin>>begin;
                bool visitedd[100]={false};
                for(int i=1;i<=g.VerNum;i++) visitedd[i]=false;
                auto dist=Dijkstra(g,begin,visitedd);
                cout<<"从"<<begin<<"到各点最短距离如下(INF表示无通路或其自身)\n";
                for(int i=1;i<=g.VerNum;i++)
                {
                    if(dist[i]==INF)
                        cout<<begin<<"->"<<i<<" INF\n";
                    else
                        cout<<begin<<"->"<<i<<" "<<dist[i]<<"\n";
                }
                system("pause");
                break;
            }
            case 8:
            {
                cout<<"Floyd求解最短路径\n";
                auto dist=Floyd(g);
                int n=g.VerNum;
                int count=0;
                cout<<"各顶点之间最短路径如下:\n";
                for(int i=1;i<n;i++){
                    for(int j=i+1;j<=n;j++)
                    {
                        if(dist[i][j]!=65535)
                            cout<<i<<"->"<<j<<"="<<dist[i][j]<<" ";
                            
                    }
                    cout<<endl;
                }

                
                system("pause");
                break;
            }
            case 9:
            {
                vector<int> ans;
                bool b=topo(g,ans);
                if(!b)
                {
                    cout<<"存在环 不存在拓朴序列.\n";
                    system("pause");
                    break;
                }
                for(auto i:ans)
                {
                    cout<<g.VerList[i].data<<" ";
                }
                system("pause");
                break;
            }

            case 10:
            {
                CriticalPath(g);
                system("pause");
                break;
            }
        }
            system("cls");
            shownemu();
            cout<<"请输入您的选择:";
            cin>>choice;
    }
    return 0;
}
/**
 *      ┌─┐       ┌─┐
*   ┌──┘ ┴───────┘ ┴──┐
*   │                 │
*   │       ───       │
*   │   >        <    │
*   │                 │
*   │   ...  ⌒  ...   │
*   │                 │
*   └───┐         ┌───┘
*       │         │
*       │         │
*       │         │
*       │         └──────────────┐
*       │                        │
*       │                        ├─┐
*       │                        ┌─┘
*       │                        │
*       └─┐  ┐  ┌───────┬──┐  ┌──┘
*         │ ─┤ ─┤       │ ─┤ ─┤
*         └──┴──┘       └──┴──┘
*                神兽保佑
*               代码无BUG
*/

createGrpAdjLinkedList.h

#include<iostream>
#include<string.h>
using namespace std;


/*以下是grpAdjLinkedList.h文件内容*/

//************************************************************//
//*    图的邻接链表表示的头文件,文件名:GraphAdjLinkList.h  *//
//*                                                          *//
//************************************************************//
#define INF 65535          //定义无穷大
#define MaxVerNum  100     //定义最大顶点个数

typedef char elementType;  //定义图中顶点的数据类型
typedef int eInfoType;     //边链表中关于边的信息的数据类型,比如,带权图中可以表示边的权值  
typedef int cellType;      //定义邻接矩阵中元素的数据类型
                           //对无权图,1-相邻(有边),0-不相邻(无边)
                           //对有权图,为边的权值,特别是无穷大。
typedef enum{UDG, UDN, DG, DN} GraphKind;  //枚举图的类型--无向图,无向网,有向图,有向网

typedef struct eNode       //边链表结点结构
{
	int adjVer;            //邻接顶点地址,此处为顶点在顶点表中序号,从1开始
	int eInfo;       //边链表中表示边的相关信息,比如表的权值  主要是权值
	struct eNode* next;    //指向边链表中的下一个结点
}EdgeNode;                 //边链表结点类型

typedef struct vNode       //顶点表中元素结构
{
	char data;      //存放图中顶点的数据
	EdgeNode* firstEdge;   //指向此顶点关联的第一条边的指针,即边链表的头指针
}VerNode;

typedef struct GraphAdjLinkList
{
	VerNode VerList[MaxVerNum+1];  //存放顶点的顺序表,数组0单元不用
	int VerNum;                    //顶点数
	int ArcNum;                    //弧(边)数
	GraphKind gKind;               //图的类型:0-无向图;1-无向网;2-有向图;3-有向网
}Graph2;                            //图的类型名


bool visited[MaxVerNum+1];  //全局数组,标记顶点是否已经被访问。0--未访问;1--已访问。数组0单元不用

//******************* 访问图中顶点的函数*********************//
//* 函数功能:打印图中顶点元素,并标记为已经访问            *//
//* 入口参数  Graph G,待访问的图;int verID 目标顶点编号   *//
//* 出口参数:无                                            *//
//* 返 回 值:空                                            *//
//* 函 数 名:visit(Graph &G, int verID)                    *//
//***********************************************************//
void visit(Graph2 &G, int verID)
{         //顶点编号从1开始,数组0单元不用
	cout<<G.VerList[verID].data<<"\t";
	visited[verID]=true;
}

//*******************  图中查找目标顶点 *********************//
//* 函数功能:给定顶点元素,在图中查找此顶点元素的编号      *//
//* 入口参数  Graph G,待访问的图;elementType v 目标顶点   *//
//* 出口参数:无                                            *//
//* 返 回 值:int。如果目标顶点存在,返回顶点编号,         *//
//*                顶点编号从1开始;否则返回-1              *//
//* 函 数 名:visit(Graph &G, int verID)                    *//
//***********************************************************//
int LocateVertex(Graph2 &G, elementType v)
{
	for(int i=1;i<=G.VerNum;i++)
	{
		if( G.VerList[i].data==v )
			return i;
	}
	return -1;
}

//搜索顶点v的第一个邻接顶点
int firstAdj(Graph2 &G, int v)
{
	EdgeNode *p;
	p=G.VerList[v].firstEdge;
	if(p)
		return p->adjVer;
	else
		return 0;
}

//搜索顶点v位于邻接点w之后的下一个邻接点
int nextAdj(Graph2 &G, int v, int w)
{
	EdgeNode *p;
	p=G.VerList[v].firstEdge;         //取顶点v的边链表头指针
	while(p->next)
	{
		if(p->adjVer==w)
			return p->next->adjVer;  //返回w之后下一个邻接点编号
		p=p->next;	
	}
	return 0;                        //未找到下一个邻接点,返回0

}


//******************** 打印图的相关信息 *********************//
//* 函数功能:打印图的相关信息                              *//
//* 入口参数  Graph G,待打印的图                           *//
//* 出口参数:无                                            *//
//* 返 回 值:空                                            *//
//* 函 数 名:printGraph(Graph &G)                          *//
//***********************************************************//
void printGraph(Graph2 &G)
{
	int i=0,j=0;
	//打印图的类型
	switch(G.gKind)
	{
	case UDG:
		cout<<"图类型:无向图"<<endl;
		break;
	case UDN:
		cout<<"图类型:无向网"<<endl;
		break;
	case DG:
  	    cout<<"图类型:有向图"<<endl;
		break;
	case DN:
		cout<<"图类型:有向网"<<endl;
		break;
	default:
		cout<<"图类型错误。"<<endl;
		break;
	}
	//打印图的顶点数
	cout<<"顶点数:"<<G.VerNum<<endl;
	//打印图的边数
	cout<<"边  数:"<<G.ArcNum<<endl;
	//打印顶点及其编号
	cout<<"编号\t顶点\t边链表"<<endl;
    EdgeNode* p;    
	for(i=1;i<=G.VerNum;i++)
	{
		cout<<i<<"\t"<<G.VerList[i].data<<"\t";
		p=G.VerList[i].firstEdge;
		while(p!=NULL)
		{
			cout<<p->adjVer<<"\t";
			p=p->next;
		}
		cout<<endl;
	}
	cout<<endl;
	//打印邻接矩阵
	// cout<<"邻接矩阵:"<<endl;
	// for(i=1;i<=G.VerNum;i++)
	// {   
	// 	cout<<"\t";
	// 	p=G.VerList[i].firstEdge;
	// 	j=1;
	// 	while(p!=NULL || j<=G.VerNum)
	// 	{
	// 		if((j<=G.VerNum) && (p!=NULL) && j==p->adjVer)  //有边
	// 		{
	// 			cout<<p->eInfo<<"\t";
	// 			j++;
	// 			p=p->next;
	// 		}
	// 		else   //无边
	// 		{
	// 			if(G.gKind==UDN || G.gKind==DN)
	// 				cout<<"INF"<<"\t";  //网,无边时打印权值为无穷大,用“INF”表示
	// 			else
	// 				cout<<"0"<<"\t";    //图,无边时用0表示
	// 			j++;			
	// 		}
			
	// 	}
	// 	cout<<endl;
	// }
}


/*以下是原文件内容*/
void strLTrim(char* str);  //删除字符串左边空格

//***************************2 文件创建图****************************//
//* 函数功能:从文本文件创建邻接矩阵表示的图                        *//
//* 入口参数  char fileName[],文件名                               *//
//* 出口参数:Graph &G,即创建的图                                  *//
//* 返 回 值:bool,true创建成功;false创建失败                     *//
//* 函 数 名:CreateGrpFromFile(char fileName[], Graph &G)          *//
//* 备    注:本函数使用的数据文件以邻接矩阵为输入数据              *//
//*******************************************************************//
int CreateGrpFromFile1(char fileName[], Graph2 &G)
{
	FILE* pFile;     //定义顺序表的文件指针
	char str[1000];  //存放读出一行文本的字符串
	char strTemp[10]; //判断是否注释行
	char* ss; 
    int i=0,j=0;
	int edgeNum=0;  //边的数量

	GraphKind graphType;  //图类型枚举变量

	pFile=fopen(fileName,"r");
	if(!pFile)
	{
		printf("错误:文件%s打开失败。\n",fileName);
		return false;
	}
	
	ss=fgets(str,1000,pFile);
	strncpy(strTemp,str,2);
	while((ss!=NULL) && (strstr(strTemp,"//")!=NULL) )  //跳过注释行
	{
		ss=fgets(str,1000,pFile);
		strncpy(strTemp,str,2);
		//cout<<strTemp<<endl;
	}
	    //循环结束,str中应该已经是文件标识,判断文件格式
	//cout<<str<<endl;
	if(strstr(str,"Graph")==NULL)
	{
		printf("错误:打开的文件格式错误!\n");
		fclose(pFile); //关闭文件
		return false;
	}
    //读取图的类型
	if(fgets(str,1000,pFile)==NULL)
	{
		printf("错误:读取图的类型标记失败!\n");
		fclose(pFile); //关闭文件
		return false;
	}
    //设置图的类型
	if(strstr(str,"UDG"))
		graphType=UDG;  //无向图
	else if(strstr(str,"UDN"))
		graphType=UDN;  //无向网
	else if(strstr(str,"DG"))
		graphType=DG;   //有向图
	else if(strstr(str,"DN"))
		graphType=DN;   //有向网
	else
	{
		printf("错误:读取图的类型标记失败!\n");
		fclose(pFile); //关闭文件
		return false;
	}

	//读取顶点元素,到str
	if(fgets(str,1000,pFile)==NULL)
	{
		printf("错误:读取图的顶点元素失败!\n");
		fclose(pFile); //关闭文件
		return false;
	}


	//顶点数据放入图的顶点数组		
	char* token=strtok(str," ");
	int nNum=1;	
	while(token!=NULL)
	{
		G.VerList[nNum].data=*token;
		G.VerList[nNum].firstEdge=NULL;
		//p=NULL;
		//eR=G.VerList[i].firstEdge;
        token = strtok( NULL, " ");
		nNum++;
	}
	
    //循环读取邻接矩阵数据
	int nRow=1;  //矩阵行下标
	int nCol=1;  //矩阵列下标
	EdgeNode* eR;  //边链表尾指针
	EdgeNode* p;    
	
	while(fgets(str,1000,pFile)!=NULL)
	{
		eR=NULL;
		p=NULL;
		nCol=1;  //列号设为0,一行重新开始
		char* token=strtok(str," ");  //以空格为分隔符,分割一行数据,写入邻接矩阵
		while(token!=NULL)
		{			
			if(atoi(token)>=1 && atoi(token)<INF)  //考虑到网
			{
				p=new EdgeNode;  //申请一个边链表结点
				p->adjVer=nCol;   //顶点的编号,从1开始
				p->eInfo=atoi(token);  //有权图保存权值,无权图为1
				p->next=NULL;
				if(G.VerList[nRow].firstEdge==NULL)
				{
					G.VerList[nRow].firstEdge=p;
					eR=p;
				}
				else
				{
					eR->next=p;
					eR=p;  //新的尾指针				
				}				
				edgeNum++;   //边数加1
			}
			token = strtok( NULL, " ");  //读取下一个子串
			nCol++;
		}
		nRow++; //一行数据处理完毕
	}

    G.VerNum=nNum;  //图的顶点数
	if(graphType==UDG || graphType==UDN)
		G.ArcNum=edgeNum / 2;  //无向图或网的边数等于统计的数字除2  
	else
		G.ArcNum=edgeNum;

	G.gKind=graphType;  //图的类型

	fclose(pFile); //关闭文件
	return true;
}

//***************************3 文件创建图****************************//
//* 函数功能:从文本文件创建邻接矩阵表示的图                        *//
//* 入口参数  char fileName[],文件名                               *//
//* 出口参数:Graph &G,即创建的图                                  *//
//* 返 回 值:bool,true创建成功;false创建失败                     *//
//* 函 数 名:CreateGraphUDFromFile(char fileName[], Graph &G)      *//
//* 备注:本函数使用的数据文件格式以边(顶点对)为基本数据          *//
//*******************************************************************//
int CreateGraphFromFile(char fileName[], Graph2 &G)//用此文件创图
{
	FILE* pFile;     //定义顺序表的文件指针
	char str[1000];  //存放读出一行文本的字符串
	char strTemp[10]; //判断是否注释行

    int i=0,j=0;
	int edgeNum=0;  //边的数量

	eInfoType eWeight;  //边的信息,常为边的权值
	GraphKind graphType;  //图类型枚举变量

	pFile=fopen(fileName,"r");
	if(!pFile)
	{
		printf("错误:文件%s打开失败。\n",fileName);
		return false;
	}
	
	while(fgets(str,1000,pFile)!=NULL)  //跳过空行和注释行
	{
		//删除字符串左边空格
		strLTrim(str);
		if (str[0]=='\n')  //空行,继续读取下一行
			continue;

		strncpy(strTemp,str,2);
		if(strstr(strTemp,"//")!=NULL)  //跳过注释行
			continue;
		else  //非注释行、非空行,跳出循环
			break;
	}

    //循环结束,str中应该已经是文件标识,判断文件格式
	if(strstr(str,"Graph")==NULL)
	{
		printf("错误:打开的文件格式错误!\n");
		fclose(pFile); //关闭文件
		return false;
	}

	//读取图的类型,跳过空行及注释行
	while(fgets(str,1000,pFile)!=NULL)
	{
		//删除字符串左边空格
		strLTrim(str);
		if (str[0]=='\n')  //空行,继续读取下一行
			continue;

		strncpy(strTemp,str,2);
		if(strstr(strTemp,"//")!=NULL)  //注释行,跳过,继续读取下一行
			continue;		
		else  //非空行,也非注释行,即图的类型标识
			break;

	}

    //设置图的类型
	if(strstr(str,"UDG"))
		graphType=UDG;  //无向图
	else if(strstr(str,"UDN"))
		graphType=UDN;  //无向网
	else if(strstr(str,"DG"))
		graphType=DG;   //有向图
	else if(strstr(str,"DN"))
		graphType=DN;   //有向网
	else
	{
		printf("错误:读取图的类型标记失败!\n");
		fclose(pFile); //关闭文件
		return false;
	}


	//读取顶点元素,到str。跳过空行
	while(fgets(str,1000,pFile)!=NULL)
	{
		//删除字符串左边空格
		strLTrim(str);
		if (str[0]=='\n')  //空行,继续读取下一行
			continue;

		strncpy(strTemp,str,2);
		if(strstr(strTemp,"//")!=NULL)  //注释行,跳过,继续读取下一行
			continue;		
		else  //非空行,也非注释行,即图的顶点元素行
			break;
	}

	//顶点数据放入图的顶点数组		
	char* token=strtok(str," ");
	int nNum=0;	
	while(token!=NULL)
	{
		G.VerList[nNum+1].data=*token;
		G.VerList[nNum+1].firstEdge=NULL;
		//p=NULL;
		//eR=G.VerList[i].firstEdge;
        token = strtok( NULL, " ");
		nNum++;
	}
	
    //循环读取边(顶点对)数据
	int nRow=1;  //矩阵行下标
	int nCol=1;  //矩阵列下标
	EdgeNode* eR;  //边链表尾指针
	EdgeNode* p;    

	elementType Nf,Ns; //边或弧的2个相邻顶点
	while(fgets(str,1000,pFile)!=NULL)
	{
		//删除字符串左边空格
		strLTrim(str);
		if (str[0]=='\n')  //空行,继续读取下一行
			continue;
		
		strncpy(strTemp,str,2);
		if(strstr(strTemp,"//")!=NULL)  //注释行,跳过,继续读取下一行
			continue;

		//nCol=0;  //列号设为0,一行重新开始
		char* token=strtok(str," ");  //以空格为分隔符,分割一行数据,写入邻接矩阵
		
		if(token==NULL)  //分割为空串,失败退出
		{
			printf("错误:读取图的边数据失败!\n");
			fclose(pFile); //关闭文件
			return false;
		}
		Nf=*token;  //获取边的第一个顶点
		
		token = strtok( NULL, " ");  //读取下一个子串,即第二个顶点
		if(token==NULL)  //分割为空串,失败退出
		{
			printf("错误:读取图的边数据失败!\n");
			fclose(pFile); //关闭文件
			return false;
		}

		Ns=*token;  //获取边的第二个顶点
            //从第一个顶点获取行号		
		for(nRow=1;nRow<=nNum;nRow++)
		{
			if(G.VerList[nRow].data==Nf)  //从顶点列表找到第一个顶点的编号
				break;
		}
           //从第二个顶点获取列号		
		for(nCol=1;nCol<=nNum;nCol++)
		{
			if(G.VerList[nCol].data==Ns)  //从顶点列表找到第二个顶点的编号
				break;
		}

		//如果为网,读取权值
		if(graphType==UDN || graphType==DN)
		{
			token = strtok( NULL, " ");  //读取下一个子串,即边的附加信息,常为边的权重
			if(token==NULL)  //分割为空串,失败退出
			{
				printf("错误:读取图的边数据失败!\n");
				fclose(pFile); //关闭文件
				return false;
			}
			eWeight=atoi(token);  //取得边的附加信息
		}


		eR=G.VerList[nRow].firstEdge;
		while(eR!=NULL && eR->next!=NULL)
		{
			eR=eR->next;  //后移边链表指针,直至尾节点
		}
		
		p=new EdgeNode;  //申请一个边链表结点
		p->adjVer=nCol;   //顶点的编号,从1开始
		if(graphType==UDN || graphType==DN) //边的附加信息,对有权图保存权值,无权图为1
			p->eInfo=eWeight;
		else
			p->eInfo=1; 
		p->next=NULL;
		
		if(G.VerList[nRow].firstEdge==NULL)
		{
			G.VerList[nRow].firstEdge=p;
			eR=p;
		}
		else
		{
			eR->next=p;
			eR=p;  //新的尾指针				
		}				

		edgeNum++;   //边数加1
	}
	


    G.VerNum=nNum;  //图的顶点数
	if(graphType==UDG || graphType==UDN)
		G.ArcNum=edgeNum / 2;  //无向图或网的边数等于统计的数字除2  
	else
		G.ArcNum=edgeNum;

	G.gKind=graphType;  //图的类型

	fclose(pFile); //关闭文件
	return true;
}

//删除字符串、字符数组左边空格
void strLTrim(char* str)
{
	int i,j;
	int n=0;
	n=strlen(str)+1;
	for(i=0;i<n;i++)
	{
		if(str[i]!=' ')  //找到左起第一个非空格位置
			break;
	}
	    //以第一个非空格字符为手字符移动字符串
	for(j=0;j<n;j++)
	{
		str[j]=str[i];
		i++;
	}
}

//销毁图
void DestroyGraph(Graph2 &G)
{
	EdgeNode *p,*u;
	int vID;
	for(vID=1; vID<=G.VerNum; vID++)  //循环删除每个顶点的边链表
	{
		p=G.VerList[vID].firstEdge;
		G.VerList[vID].firstEdge=NULL;
		while(p)  //循环删除当前顶点所有的关联边 
		{
			u=p->next;  //u指向下一个边结点
			delete(p);  //删除当前边结点
			p=u;
		}		
	}
	p=NULL;
	u=NULL;
	G.VerNum=-1;  //编辑图已经销毁
}

运行无

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值