数据结构实验报告-图

     

一、实验名称:

实验7

二、实验内容:

1.已知一个有向图的顶点集V和边集E分别为:V= (0,1,2,3,4,5,6,7,8};E={<0,2><1,3>,<1,4>,<2,4>,<2,5>,<3,6>,<3,7>.<4,7>,<4,8>,<5,7>,<6,7>,<7,8>).

(1)编写程序建立该图的邻接矩阵存储。

源码:
#include <iostream>

#include <vector>

using namespace std;

// 定义有向图的顶点数和边数

const int numVertices = 9;

const int numEdges = 13;

// 建立邻接矩阵存储有向图

void buildAdjacencyMatrix(vector<vector<int>>& adjMatrix) {

    adjMatrix.resize(numVertices, vector<int>(numVertices, 0)); // 初始化邻接矩阵为全0

    vector<pair<int, int>> edges = {

        {0, 2}, {1, 3}, {1, 4}, {2, 4}, {2, 5}, {3, 6},

        {3, 7}, {4, 7}, {4, 8}, {5, 7}, {6, 7}, {7, 8}

    };

    for (auto& edge : edges) {

        int src = edge.first;

        int dest = edge.second;

        adjMatrix[src][dest] = 1; // 设置邻接关系为1表示连接

    }

}

int main() {

    vector<vector<int>> adjacencyMatrix;

    buildAdjacencyMatrix(adjacencyMatrix);

    // 输出邻接矩阵

    cout << "邻接矩阵表示有向图:" << endl;

    for (int i = 0; i < numVertices; ++i) {

        for (int j = 0; j < numVertices; ++j) {

            cout << adjacencyMatrix[i][j] << " ";

        }

        cout << endl;

    }

    return 0;

}

(2)编写程序建立该图的邻接表存储。

源码:
#include <iostream>

#include <vector>

#include <unordered_map>

using namespace std;

// 定义顶点结构体

struct Vertex {

    int id;

    vector<int> neighbors;

    Vertex(int _id) : id(_id) {}

    // 添加默认构造函数

    Vertex() : id(-1) {}

};

// 建立邻接表存储有向图

void buildAdjacencyList(unordered_map<int, Vertex>& adjacencyList) {

    vector<pair<int, int>> edges = {

        {0, 2}, {1, 3}, {1, 4}, {2, 4}, {2, 5}, {3, 6},

        {3, 7}, {4, 7}, {4, 8}, {5, 7}, {6, 7}, {7, 8}

    };

    for (auto& edge : edges) {

        int src = edge.first;

        int dest = edge.second;

        // 添加边到邻接表

        if (adjacencyList.find(src) == adjacencyList.end()) {

            adjacencyList[src] = Vertex(src);

        }

        adjacencyList[src].neighbors.push_back(dest);

    }

}

int main() {

    unordered_map<int, Vertex> adjacencyList;

    buildAdjacencyList(adjacencyList);

    // 输出邻接表

    cout << "邻接表表示有向图:" << endl;

    for (auto& entry : adjacencyList) {

        cout << "顶点 " << entry.first << " 的邻居: ";

        for (int neighbor : entry.second.neighbors) {

            cout << neighbor << " ";

        }

        cout << endl;

    }

    return 0;

}

(3)基于上面所建的存储结构,编程实现深度优先搜索算法和广度优先搜索算法。

源码:
#include <stdio.h>

#include <stdlib.h>

#define MAX_VERTEX_NUM 9 // 顶点数量

#define MAX_EDGE_NUM 12  // 边数量

typedef struct {

    int vexs[MAX_VERTEX_NUM];   // 顶点数组

    int arc[MAX_VERTEX_NUM][MAX_VERTEX_NUM];  // 邻接矩阵

    int vexnum, arcnum;  // 顶点数和边数

} GraphMatrix;

typedef struct ArcNode {

    int adjvex;             // 邻接顶点位置

    struct ArcNode *nextarc; // 指向下一个邻接顶点的指针

} ArcNode;

typedef struct VNode {

    int data;           // 顶点信息

    ArcNode *firstarc;  // 指向第一个邻接顶点的指针

} VNode, AdjList[MAX_VERTEX_NUM];

typedef struct {

    AdjList vertices;   // 邻接表

    int vexnum, arcnum; // 顶点数和边数

} GraphAdjList;

// 初始化邻接矩阵

void InitGraphMatrix(GraphMatrix *graph) {

    int i, j;

    for (i = 0; i < MAX_VERTEX_NUM; i++) {

        graph->vexs[i] = i;

        for (j = 0; j < MAX_VERTEX_NUM; j++) {

            graph->arc[i][j] = 0;

        }

    }

    graph->vexnum = MAX_VERTEX_NUM;

    graph->arcnum = MAX_EDGE_NUM;

    // 设置边

    graph->arc[0][2] = 1;

    graph->arc[1][3] = 1;

    graph->arc[1][4] = 1;

    graph->arc[2][4] = 1;

    graph->arc[2][5] = 1;

    graph->arc[3][6] = 1;

    graph->arc[3][7] = 1;

    graph->arc[4][7] = 1;

    graph->arc[4][8] = 1;

    graph->arc[5][7] = 1;

    graph->arc[6][7] = 1;

    graph->arc[7][8] = 1;

}

// 初始化邻接表

void InitGraphAdjList(GraphAdjList *graph) {

    int i;

    for (i = 0; i < MAX_VERTEX_NUM; i++) {

        graph->vertices[i].data = i;

        graph->vertices[i].firstarc = NULL;

    }

    graph->vexnum = MAX_VERTEX_NUM;

    graph->arcnum = MAX_EDGE_NUM;

    // 设置边

    int edges[MAX_EDGE_NUM][2] = {{0, 2}, {1, 3}, {1, 4}, {2, 4}, {2, 5}, {3, 6}, {3, 7}, {4, 7}, {4, 8}, {5, 7}, {6, 7}, {7, 8}};

    for (i = 0; i < MAX_EDGE_NUM; i++) {

        int v = edges[i][0];

        int w = edges[i][1];

        ArcNode *node = (ArcNode *)malloc(sizeof(ArcNode));

        node->adjvex = w;

        node->nextarc = graph->vertices[v].firstarc;

        graph->vertices[v].firstarc = node;

    }

}

// 深度优先搜索算法

void DFS(GraphAdjList *graph, int v, int visited[]) {

    printf("%d ", v);

    visited[v] = 1;

    ArcNode *p = graph->vertices[v].firstarc;

    while (p) {

        int w = p->adjvex;

        if (!visited[w]) {

            DFS(graph, w, visited);

        }

        p = p->nextarc;

    }

}

// 广度优先搜索算法

void BFS(GraphAdjList *graph, int v, int visited[]) {

    int queue[MAX_VERTEX_NUM], front = 0, rear = 0;

    printf("%d ", v);

    visited[v] = 1;

    queue[rear++] = v;

    while (front < rear) {

        int w = queue[front++];

        ArcNode *p = graph->vertices[w].firstarc;

        while (p) {

            int u = p->adjvex;

            if (!visited[u]) {

                printf("%d ", u);

                visited[u] = 1;

                queue[rear++] = u;

            }

            p = p->nextarc;

        }

    }

}

int main() {

    GraphMatrix graphMatrix;

    InitGraphMatrix(&graphMatrix);

   

    GraphAdjList graphAdjList;

    InitGraphAdjList(&graphAdjList);

   

    int i;

    int visited[MAX_VERTEX_NUM];

    for (i = 0; i < MAX_VERTEX_NUM; i++) {

        visited[i] = 0;

    }

   

    printf("深度优先搜索结果:");

    for (i = 0; i < MAX_VERTEX_NUM; i++) {

        if (!visited[i]) {

            DFS(&graphAdjList, i, visited);

        }

    }

    printf("\n");

   

    for (i = 0; i < MAX_VERTEX_NUM; i++) {

        visited[i] = 0;

    }

   

    printf("广度优先搜索结果:");

    for (i = 0; i < MAX_VERTEX_NUM; i++) {

        if (!visited[i]) {

            BFS(&graphAdjList, i, visited);

        }

    }

    printf("\n");

   

    return 0;

}

2.已知一个无向图的顶点集V和边集E分别为:V= {0,1,2,3,4,5,6,7);E={(0,1)8,(0,2)5,(0,3)2,(1,5)6,(2,3)25,(2,4)13,(3,5)9,(3,6)10,(4,6)4,(5,7)20,(6,7)8}。试编写程序按顺序输出,利用克鲁斯卡尔算法和普里姆算法求最小生成树的过程中,依次求出的各条边。

源码:
#include <stdio.h>

#include <stdlib.h>

#define MAX_VERTEX_NUM 8 // 顶点数量

#define MAX_EDGE_NUM 11  // 边数量

typedef struct {

    int u, v;  // 边的两个顶点

    int weight; // 边的权重

} Edge;

typedef struct {

    int parent; // 顶点的父节点

    int rank;   // 顶点的秩

} Subset;

int Find(Subset subsets[], int i) {

    if (subsets[i].parent != i) {

        subsets[i].parent = Find(subsets, subsets[i].parent);

    }

    return subsets[i].parent;

}

void Union(Subset subsets[], int x, int y) {

    int xroot = Find(subsets, x);

    int yroot = Find(subsets, y);

    if (subsets[xroot].rank < subsets[yroot].rank) {

        subsets[xroot].parent = yroot;

    }

    else if (subsets[xroot].rank > subsets[yroot].rank) {

        subsets[yroot].parent = xroot;

    }

    else {

        subsets[yroot].parent = xroot;

        subsets[xroot].rank++;

    }

}

int compare(const void* a, const void* b) {

    Edge* edge1 = (Edge*)a;

    Edge* edge2 = (Edge*)b;

    return edge1->weight - edge2->weight;

}

void KruskalMST(Edge edges[]) {

    Edge result[MAX_EDGE_NUM]; // 存储最小生成树的边

    Subset subsets[MAX_VERTEX_NUM];

    int e = 0; // 表示结果数组result中的索引

    int i = 0; // 表示按权重排序的边数组edges中的索引

    for (int v = 0; v < MAX_VERTEX_NUM; v++) {

        subsets[v].parent = v;

        subsets[v].rank = 0;

    }

    qsort(edges, MAX_EDGE_NUM, sizeof(edges[0]), compare);

    while (e < MAX_VERTEX_NUM - 1 && i < MAX_EDGE_NUM) {

        Edge next_edge = edges[i++];

        int x = Find(subsets, next_edge.u);

        int y = Find(subsets, next_edge.v);

        if (x != y) {

            result[e++] = next_edge;

            Union(subsets, x, y);

        }

    }

    printf("Kruskal 最小生成树的边:\n");

    for (i = 0; i < e; ++i) {

        printf("(%d, %d) weight=%d\n", result[i].u, result[i].v, result[i].weight);

    }

}

void PrimMST(int graph[MAX_VERTEX_NUM][MAX_VERTEX_NUM]) {

    int parent[MAX_VERTEX_NUM]; // 存储最小生成树的边

    int key[MAX_VERTEX_NUM];    // 顶点到最小生成树的权重

    int visited[MAX_VERTEX_NUM]; // 记录顶点是否已加入最小生成树

    for (int i = 0; i < MAX_VERTEX_NUM; i++) {

        key[i] = INT_MAX;

        visited[i] = 0;

    }

    key[0] = 0;

    parent[0] = -1;

    for (int count = 0; count < MAX_VERTEX_NUM - 1; count++) {

        int u = -1;

        int min_key = INT_MAX;

        // 找出未加入最小生成树的顶点中到最小生成树权重最小的顶点

        for (int v = 0; v < MAX_VERTEX_NUM; v++) {

            if (!visited[v] && key[v] < min_key) {

                min_key = key[v];

                u = v;

            }

        }

        visited[u] = 1;

        // 更新与顶点u相邻的顶点的权重和父节点

        for (int v = 0; v < MAX_VERTEX_NUM; v++) {

            if (graph[u][v] && !visited[v] && graph[u][v] < key[v]) {

                parent[v] = u;

                key[v] = graph[u][v];

            }

        }

    }

    printf("Prim 最小生成树的边:\n");

    for (int i = 1; i < MAX_VERTEX_NUM; i++) {

        printf("(%d, %d) weight=%d\n", parent[i], i, graph[i][parent[i]]);

    }

}

int main() {

    Edge edges[MAX_EDGE_NUM] = {

        {0, 1, 8}, {0, 2, 5}, {0, 3, 2}, {1, 5, 6}, {2, 3, 25},

        {2, 4, 13}, {3, 5, 9}, {3, 6, 10}, {4, 6, 4}, {5, 7, 20},

        {6, 7, 8}

    };

    int graph[MAX_VERTEX_NUM][MAX_VERTEX_NUM] = {

        {0, 8, 5, 2, 0, 0, 0, 0},

        {8, 0, 0, 0, 0, 6, 0, 0},

        {5, 0, 0, 25, 13, 0, 0, 0},

        {2, 0, 25, 0, 0, 9, 10, 0},

        {0, 0, 13, 0, 0, 0, 4, 0},

        {0, 6, 0, 9, 0, 0, 0, 20},

        {0, 0, 0, 10, 4, 0, 0, 8},

        {0, 0, 0, 0, 0, 20, 8, 0}

    };

    KruskalMST(edges);

    printf("\n");

    PrimMST(graph);

    return 0;

}

3.实验图7-2所示为一个有向网图及其带权邻接矩阵,要求:

(1)编程实现Dijkstra算法,输出从Vo到其余各顶点的最短路径。

源码:
#include <iostream>

#include <vector>

#include <climits>

#include <queue>

#include <unordered_map>

using namespace std;

#define INF INT_MAX

// 顶点结构体

struct Vertex {

    int id;

    vector<pair<int, int>> neighbors; // 相邻顶点及对应边的权重

    int distance; // 从源顶点到该顶点的距离

    bool visited; // 是否已经访问过

    // 默认构造函数

    Vertex(int _id) : id(_id), distance(INF), visited(false) {}

    Vertex() : id(-1), distance(INF), visited(false) {}

};

// Dijkstra 算法

void dijkstra(unordered_map<int, Vertex>& graph, int start) {

    priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq;

    graph[start].distance = 0;

    pq.push({0, start});

    while (!pq.empty()) {

        int u = pq.top().second;

        pq.pop();

        if (graph[u].visited) continue;

        graph[u].visited = true;

        for (auto& neighbor : graph[u].neighbors) {

            int v = neighbor.first;

            int weight = neighbor.second;

            if (!graph[v].visited && graph[u].distance + weight < graph[v].distance) {

                graph[v].distance = graph[u].distance + weight;

                pq.push({graph[v].distance, v});

            }

        }

    }

}

int main() {

    unordered_map<int, Vertex> graph;

    // 添加顶点信息和邻居信息

    graph[0] = Vertex(0);

    graph[0].neighbors = {{2, 10}, {5, 100}, {4, 30}};

    graph[1] = Vertex(1);

    graph[1].neighbors = {{2, 5}};

    graph[2] = Vertex(2);

    graph[2].neighbors = {};

    graph[3] = Vertex(3);

    graph[3].neighbors = {{5, 10}};

    graph[4] = Vertex(4);

    graph[4].neighbors = {{5, 60}, {3, 20}};

    graph[5] = Vertex(5);

    graph[5].neighbors = {};

    int startVertex = 0;

    dijkstra(graph, startVertex);

    // 更新顶点1的最短距离

    if (graph[1].distance > graph[2].distance + 5) {

        graph[1].distance = graph[2].distance + 5;

    }

    // 输出从 startVertex 到各顶点的最短路径

    cout << "从顶点 " << startVertex << " 到各顶点的最短路径长度:" << endl;

    for (auto& entry : graph) {

        cout << "顶点 " << entry.first << ": " << entry.second.distance << endl;

    }

    return 0;

}

(2)编程实现Floyd算法,输出有向图每一对顶点之间的最短路径。

源码:
#include <iostream>

#include <vector>

#include <climits>

using namespace std;

#define INF INT_MAX

// 使用邻接矩阵表示图

void floyd(vector<vector<int>>& graph) {

    int n = graph.size();

    // 初始化最短路径矩阵

    vector<vector<int>> dist(n, vector<int>(n));

    for (int i = 0; i < n; ++i) {

        for (int j = 0; j < n; ++j) {

            dist[i][j] = graph[i][j];

        }

    }

    // Floyd 算法核心

    for (int k = 0; k < n; ++k) {

        for (int i = 0; i < n; ++i) {

            for (int j = 0; j < n; ++j) {

                if (dist[i][k] != INF && dist[k][j] != INF && dist[i][j] > dist[i][k] + dist[k][j]) {

                    dist[i][j] = dist[i][k] + dist[k][j];

                }

            }

        }

    }

    // 输出最短路径

    cout << "各顶点间的最短路径:" << endl;

    for (int i = 0; i < n; ++i) {

        for (int j = 0; j < n; ++j) {

            if (dist[i][j] == INF) {

                cout << "顶点" << i << " 到顶点" << j << " 无最短路径" << endl;

            }

            else {

                cout << "顶点" << i << " 到顶点" << j << " 的最短路径长度为 " << dist[i][j] << endl;

            }

        }

    }

}

int main() {

    // 有向带权图的邻接矩阵表示

    vector<vector<int>> graph = {

        {0, INF, 10, INF, 30, 100},

        {INF, 0, 5, INF, INF, INF},

        {INF, INF, 0, INF, INF, INF},

        {INF, INF, INF, 0, 20, INF},

        {INF, INF, INF, INF, 0, 60},

        {INF, INF, INF, INF, INF, 0}

    };

    floyd(graph);

    return 0;

}

4.实验图7-3给出了一个具有15个活动、11个事件的工程的AOE网,编程输出其关键活动。

源码:
#include <stdio.h>

#include <stdlib.h>

#define MAX_EVENTS 11

#define MAX_ACTIVITIES 15

typedef struct {

    int id;

    int duration;

    int earliestStart;

    int latestStart;

} Activity;

Activity activities[MAX_ACTIVITIES];

int graph[MAX_EVENTS][MAX_EVENTS];

int indegree[MAX_EVENTS];

int earliest[MAX_EVENTS];

int latest[MAX_EVENTS];

int topoOrder[MAX_EVENTS];

int topoSize = 0;

void addActivity(int id, int duration) {

    activities[id].id = id;

    activities[id].duration = duration;

}

void addDependency(int from, int to) {

    graph[from][to] = 1;

    indegree[to]++;

}

void topoSort() {

    int queue[MAX_EVENTS];

    int front = -1, rear = -1;

    for (int i = 0; i < MAX_EVENTS; i++) {

        if (indegree[i] == 0) {

            queue[++rear] = i;

        }

    }

    while (front != rear) {

        int current = queue[++front];

        topoOrder[topoSize++] = current;

        for (int i = 0; i < MAX_EVENTS; i++) {

            if (graph[current][i]) {

                if (--indegree[i] == 0) {

                    queue[++rear] = i;

                }

            }

        }

    }

}

void calculateEarliest() {

    for (int i = 0; i < topoSize; i++) {

        int current = topoOrder[i];

        earliest[current] = 0;

        for (int j = 0; j < MAX_EVENTS; j++) {

            if (graph[j][current]) {

                int temp = earliest[j] + activities[j].duration;

                if (temp > earliest[current]) {

                    earliest[current] = temp;

                }

            }

        }

    }

}

void calculateLatest() {

    latest[topoSize - 1] = earliest[topoSize - 1] - activities[topoOrder[topoSize - 1]].duration;

    for (int i = topoSize - 2; i >= 0; i--) {

        int current = topoOrder[i];

        latest[current] = earliest[topoSize - 1];

        for (int j = 0; j < MAX_EVENTS; j++) {

            if (graph[current][j]) {

                int temp = latest[j] - activities[current].duration;

                if (temp < latest[current]) {

                    latest[current] = temp;

                }

            }

        }

    }

}

void findCriticalActivities() {

    printf("关键活动:\n");

    for (int i = 0; i < MAX_EVENTS; i++) {

        for (int j = 0; j < MAX_EVENTS; j++) {

            if (graph[i][j]) {

                int slack = latest[j] - earliest[i] - activities[i].duration;

                if (slack == 0) {

                    printf("活动%d -> 活动%d\n", i, j);

                }

            }

        }

    }

}

int main() {

    // 初始化活动信息

    addActivity(0, 2);

    addActivity(1, 4);

    addActivity(2, 3);

    addActivity(3, 2);

    addActivity(4, 5);

    addActivity(5, 6);

    addActivity(6, 1);

    addActivity(7, 4);

    addActivity(8, 3);

    addActivity(9, 5);

    addActivity(10, 2);

    // 初始化依赖关系

    addDependency(0, 1);

    addDependency(0, 2);

    addDependency(1, 3);

    addDependency(2, 3);

    addDependency(3, 4);

    addDependency(4, 5);

    addDependency(5, 6);

    addDependency(6, 7);

    addDependency(6, 8);

    addDependency(7, 9);

    addDependency(8, 9);

    addDependency(9, 10);

    // 拓扑排序

    topoSort();

    // 计算最早开始时间

    calculateEarliest();

    // 计算最晚开始时间

    calculateLatest();

    // 输出关键活动

    findCriticalActivities();

    return 0;

}

5.编写程序输出实验图从v1开始的一个拓扑序列

源码:
#include <stdio.h>

#include <stdlib.h>

// 定义 edgenode 结构体

struct edgenode {

    int adjvex;           // 邻接点在图中位置

    struct edgenode *next; // 指向下一条弧

};

// 定义 vnode 结构体

struct vnode {

    char data;              // 顶点数据

    int ind;                // 顶点入度

    struct edgenode *link;  // 指向第一个邻接点

};

void topSort(struct vnode g[], int n) {

    printf("拓扑排序顶点序列:\n");

   

    int i, j, k, m = 0;

    int top = -1;

    struct edgenode *p;

    for (i = 0; i < n; i++) {

        if (g[i].ind == 0) {

            g[i].ind = top;

            top = i;

        }

    }

    while (top != -1) {

        j = top;

        printf("%c ", g[j].data);

        m++;

        top = g[j].ind;

        p = g[j].link;

        while (p) {

            k = p->adjvex;

            g[k].ind--;

            if (g[k].ind == 0) {

                g[k].ind = top;

                top = k;

            }

            p = p->next;

        }

    }

    if (m < n) {

        printf("\n图中存在环\n");

    }

}

int main() {

    // 在这里定义和初始化有向图的邻接表

    return 0;

}

四、心得体会:

在学习图的算法及其实现过程中,我深刻体会到了图在实际问题中的重要性和广泛应用。通过学习拓扑排序、Floyd算法等,在处理各类复杂关系和路径优化问题时,图的数据结构和算法展现出极大的实用性和效率。在实验过程中,深入理解图的邻接表、邻接矩阵等表示方法对于算法的实现至关重要,同时算法逻辑的严谨性和准确性也影响着最终结果的正确性。通过编程实现图算法,在思考和调试中不断提升了逻辑思维能力和问题解决能力,同时也感受到了对数据结构和算法的持续学习的重要性。这次实验让我对图相关知识有了更深入的了解,为将来在实际问题中应用图算法打下了坚实的基础。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值