【问题描述】
在本问题,定义了图的两种表示方法:邻接表(链表)表示法及邻接矩阵表示法。
头文件graph.h定义了图的ADT类Graph,头文件grlist.h中定义的类Graphl,实现了使用图的邻接表表示法,头文件grmat.h中定义的类Graphm,实现了图的邻接矩阵表示法,都是Graph类的子类,分别实现了Graph类中定义的所有纯虚方法。
头文件Graph_test.h中的类option定义了图的相关算法。
头文件link.h、list.h、llist.h定义了链表的相关操作。
文件main.cpp为主函数源文件,其中包含了所有针对图的输入以及所有测试选项。
graphAlg.cbp为基于Code::Blocks的项目文件。
你需要下载压缩文件,创建一个文件夹,将该文件解压。无论你采用哪种编译器(Code::Blocks或Dev Cpp),都需要创建或打开项目,并将所有头文件以及源代码加入到项目之中。
你的任务是:
你需要打开头文件grlist.h以及grmat.h,在函数getInDegree以及getOutDegree处填写相关代码,测试完成第1项任务。
你需要打开头文件Graph_test.h,填写相关代码,完成第2~5项任务。
除以上文件相应的位置外,请不要改变其他任何代码,否则将可能不能通过测试。
代码填写时,不必完成所有任务,任何任务都可以单独测试,建议你一项一项完成,提交代码时请将所有头文件以及源文件打包压缩提交。
测试用例有20个:
1~4:用于测试入度/出度
5~8:用于测试DFS算法
9~12:用于测试BFS算法
13~16:用于测试Dijkstra算法
17~20:用于测试prim算法
-
-
求所有顶点的入度和出度
-
深度优先搜索(遍历)
-
广度优先搜索(遍历)
-
Dijkstra求最短路(从0点出发至其他各点)
-
prim算法求最小支撑树(从0点出发至其他各点)
-
【输入形式】
输入的第一行为一个整数choice,表示需要测试(执行)的操作或算法
1.求所有顶点的入度和出度
2.深度优先搜索(遍历)
3.广度优先搜索(遍历)
4.Dijkstra求最短路(从0点出发至其他各点)
5.prim算法求最小支撑树(从0点出发至其他各点)
输入的第二行为一个整数vert_num,表示树的节点数,编号从0~vert_num-1
输入的第三行为一个整数graph_type,表示用哪种方法存储图:
0.用邻接矩阵表示法
1.用邻接表表示法
输入的第四行为一个字符graph_dir:
U.无向图
D.有向图
接下来的若干行,每行三个参数,表示图的边,第一个参数为出点,第二个参数为入点,第三个参数为边长(权重)
【注意】输入结束务必使用:“回车 Ctrl+Z 回车”的方式
【输出形式】
根据第一个参数choice的选择不同,输出对应不同的结果,具体请参照样例输出。
【样例输入1】
1 6 1 U 0 4 9 0 2 7 2 3 1 2 1 5 2 5 2 1 5 6 3 5 2
【样例输出1】
2 2 4 2 1 3 2 2 4 2 1 3
【样例输入2】
2 6 1 U 0 4 9 0 2 7 2 3 1 2 1 5 2 5 2 1 5 6 3 5 2
【样例输出2】
PreVisit vertex 0 Visiting vertex 0 PreVisit vertex 2 Visiting vertex 2 PreVisit vertex 1 Visiting vertex 1 PreVisit vertex 5 Visiting vertex 5 PreVisit vertex 3 Visiting vertex 3 PostVisit vertex 3 PostVisit vertex 5 PostVisit vertex 1 PostVisit vertex 2 PreVisit vertex 4 Visiting vertex 4 PostVisit vertex 4 PostVisit vertex 0
【样例输入3】
3 6 1 U 0 4 9 0 2 7 2 3 1 2 1 5 2 5 2 1 5 6 3 5 2
【样例输出3】
PreVisit vertex 0 Visiting vertex 0 PreVisit vertex 2 PreVisit vertex 4 PostVisit vertex 0 Visiting vertex 2 PreVisit vertex 1 PreVisit vertex 3 PreVisit vertex 5 PostVisit vertex 2 Visiting vertex 4 PostVisit vertex 4 Visiting vertex 1 PostVisit vertex 1 Visiting vertex 3 PostVisit vertex 3 Visiting vertex 5 PostVisit vertex 5
【样例输入4】
4 6 1 U 0 4 9 0 2 7 2 3 1 2 1 5 2 5 2 1 5 6 3 5 2
【样例输出4】
0 12 7 8 9 9
【样例输入5】
5 6 1 U 0 4 9 0 2 7 2 3 1 2 1 5 2 5 2 1 5 6 3 5 2
【样例输出5】
Add edge 0 to 2 Add edge 2 to 3 Add edge 2 to 5 Add edge 2 to 1 Add edge 0 to 4 0 5 1 1 9 2
【具体代码】
1.main.cpp(主函数)
#include "grlist.h"
#include "grmat.h"
#include "Graph_test.h"
Graph *createGraph(int graph_type, int vert_num);
void PreVisit(int v)
{
cout << "PreVisit vertex " << v << "\n";
}
void PostVisit(int v)
{
cout << "PostVisit vertex " << v << "\n";
}
void Visiting(int v)
{
cout << "Visiting vertex " << v << "\n";
}
int main()
{
int choice;
cin >> choice; // 选择操作
Graph* G;
int vert_num; // 图的顶点数,编号从0开始
cin >> vert_num;
int graph_type = 2; // graph_type=1, 采用邻接链表表示图 graph_type=0, 采用邻接矩阵表示图
while(graph_type != 0 && graph_type != 1)
cin >> graph_type;
G = createGraph(graph_type, vert_num);
char graph_dir = '0'; // graph_dir='D'为有向图 graph_dir='U'为无向图
while(graph_dir != 'D' && graph_dir != 'U')
cin >> graph_dir;
int fr_vert, to_vert, wt;
while(cin >> fr_vert >> to_vert >> wt)
{
G->setEdge(fr_vert, to_vert, wt);
if (graph_dir == 'U')
G->setEdge(to_vert, fr_vert, wt);
}
option *it = new option(G);
for (int v = 0; v < G->n(); v++)
G->setMark(v, UNVISITED); // Initialize mark bits
int D[G->n()];
switch(choice)
{
case 1: //求所有节点的入度和出度
for(int i = 0; i < G->n(); i++)
cout << G->getInDegree(i) << " ";
cout << endl;
for(int i = 0; i < G->n(); i++)
cout << G->getOutDegree(i) << " ";
cout << endl;
break;
case 2: // 深度优先搜索
for (int v = 0; v < G->n(); v++)
{
if (G->getMark(v) == UNVISITED)
it->DFS(v, PreVisit, PostVisit, Visiting);
}
break;
case 3: //广度优先搜索
for (int v = 0; v < G->n(); v++)
{
if (G->getMark(v) == UNVISITED)
it->BFS(v, PreVisit, PostVisit, Visiting);
}
break;
case 4: // Dijkstra求最短路(从0点出发至其他各点)
for (int i = 0; i < G->n(); i++) // Initialize
D[i] = INFINITY;
D[0] = 0;
it->Dijkstra1(D, 0);
for(int k = 0; k < G->n(); k++)
{
if (D[k] != INFINITY)
cout << D[k] << " ";
else
cout << "Infinity" << " ";
}
cout << endl;
break;
case 5: // prim算法求最小支撑树(从0点出发至其他各点)
for (int i = 0; i < G->n(); i++) // Initialize
D[i] = INFINITY;
D[0] = 0;
it->Prim(D, 0);
for(int k = 0; k < G->n(); k++)
cout << D[k] << " ";
cout << endl;
break;
}
return 0;
}
Graph *createGraph(int graph_type, int vert_num)
{
Graph *g;
if (graph_type)
g = new Graphl(vert_num);
else
g = new Graphm(vert_num);
return g;
}
2.Graph_test.h(图的相关算法)
#ifndef GRAPH_TEST_H_INCLUDED
#define GRAPH_TEST_H_INCLUDED
#define INFINITY 1000000
#include <queue>
class option
{
private:
Graph *G;
public:
// Start with some implementations for the abstract functions
option(Graph *g)
{
G = g;
}
void DFS(int v, void (*PreVisit)(int v), void (*PostVisit)(int v), void (*Visiting)(int v)) // Depth first search
{
PreVisit(v);
G->setMark(v,VISITED);
Visiting(v);
for(int w=G->first(v);w<G->n();w=G->next(v,w))
{
if(G->getMark(w)==UNVISITED)
DFS(w,PreVisit,PostVisit,Visiting);
}
PostVisit(v);
}
void BFS(int start, void (*PreVisit)(int v), void (*PostVisit)(int v), void (*Visiting)(int v))
{
int v,w;
queue<int> q;
PreVisit(start);
G->setMark(start,VISITED);
q.push(start);
while(!q.empty())
{
v=q.front();
q.pop();
Visiting(v);
for(w=G->first(v);w<G->n();w=G->next(v,w))
{
if(G->getMark(w)==UNVISITED)
{
PreVisit(w);
G->setMark(w,VISITED);
q.push(w);
}
}
PostVisit(v);
}
}
void Dijkstra1(int* D, int s)
{
int v,w;
for(int i=0;i<G->n();i++)
{
v=minVertex(D);
if(D[v]==INFINITY) return;
G->setMark(v,VISITED);
for(w=G->first(v);w<G->n();w=G->next(v,w))
{
if(D[w]>D[v]+G->weight(v,w))
D[w]=D[v]+G->weight(v,w);
}
}
}
int minVertex(int* D) // Find min cost vertex
{
int i, v = -1;
// Initialize v to some unvisited vertex
for (i = 0; i < G->n(); i++)
if (G->getMark(i) == UNVISITED)
{
v = i;
break;
}
for (i++; i < G->n(); i++) // Now find smallest D value
if ((G->getMark(i) == UNVISITED) && (D[i] < D[v]))
v = i;
return v;
}
void AddEdgetoMST(int v1, int v2)
{
cout << "Add edge " << v1 << " to " << v2 << "\n";
}
void Prim(int* D, int s) // Prim's MST algorithm
{
int V[G->n()];
int v,w;
for(int i=0;i<G->n();i++)
{
v=minVertex(D);
if(D[v]==INFINITY) return;
G->setMark(v,VISITED);
if(v!=s) AddEdgetoMST(V[v],v);
for(w=G->first(v);w<G->n();w=G->next(v,w))
{
if(D[w]>G->weight(v,w))
{
D[w]=G->weight(v,w);
V[w]=v;
}
}
}
}
};
#endif // GRAPH_TEST_H_INCLUDED
3.graph.h(图的ADT类)
// From the software distribution accompanying the textbook
// "A Practical Introduction to Data Structures and Algorithm Analysis,
// Third Edition (C++)" by Clifford A. Shaffer.
// Source code Copyright (C) 2007-2011 by Clifford A. Shaffer.
// Graph abstract class. This ADT assumes that the number
// of vertices is fixed when the graph is created.
#ifndef GRAPH
#define GRAPH
class Graph {
private:
void operator =(const Graph&) {} // Protect assignment
Graph(const Graph&) {} // Protect copy constructor
public:
Graph() {} // Default constructor
virtual ~Graph() {} // Base destructor
// Initialize a graph of n vertices
virtual void Init(int n) =0;
// Return: the number of vertices and edges
virtual int n() =0;
virtual int e() =0;
// Return v's first neighbor
virtual int first(int v) =0;
// Return v's next neighbor
virtual int next(int v, int w) =0;
// Set the weight for an edge
// i, j: The vertices
// wgt: Edge weight
virtual void setEdge(int v1, int v2, int wght) =0;
// Delete an edge
// i, j: The vertices
virtual void delEdge(int v1, int v2) =0;
// Determine if an edge is in the graph
// i, j: The vertices
// Return: true if edge i,j has non-zero weight
virtual bool isEdge(int i, int j) =0;
// Return an edge's weight
// i, j: The vertices
// Return: The weight of edge i,j, or zero
virtual int weight(int v1, int v2) =0;
// Get and Set the mark value for a vertex
// v: The vertex
// val: The value to set
virtual int getMark(int v) =0;
virtual void setMark(int v, int val) =0;
virtual int getInDegree(int v) = 0;
virtual int getOutDegree(int v) = 0;
};
#endif // GRAPH
4.grmat.h(邻接矩阵表示法)
// From the software distribution accompanying the textbook
// "A Practical Introduction to Data Structures and Algorithm Analysis,
// Third Edition (C++)" by Clifford A. Shaffer.
// Source code Copyright (C) 2007-2011 by Clifford A. Shaffer.
// Include this file to access Graph representation implemented using an
// Adjacency Matrix.
#ifndef GRMATH
#define GRMATH
#include <stdio.h>
#include <ctype.h>
#include <assert.h>
// Used by the mark array
#define UNVISITED 0
#define VISITED 1
#include "graph.h"
// Implementation for the adjacency matrix representation
class Graphm : public Graph
{
private:
int numVertex, numEdge; // Store number of vertices, edges
int **matrix; // Pointer to adjacency matrix
int *mark; // Pointer to mark array
public:
Graphm(int numVert) // Constructor
{
Init(numVert);
}
~Graphm() // Destructor
{
delete [] mark; // Return dynamically allocated memory
for (int i = 0; i < numVertex; i++)
delete [] matrix[i];
delete [] matrix;
}
void Init(int n) // Initialize the graph
{
int i;
numVertex = n;
numEdge = 0;
mark = new int[n]; // Initialize mark array
for (i = 0; i < numVertex; i++)
mark[i] = UNVISITED;
matrix = (int**) new int*[numVertex]; // Make matrix
for (i = 0; i < numVertex; i++)
matrix[i] = new int[numVertex];
for (i = 0; i < numVertex; i++) // Initialize to 0 weights
for (int j = 0; j < numVertex; j++)
matrix[i][j] = 0;
}
int n()
{
return numVertex; // Number of vertices
}
int e()
{
return numEdge; // Number of edges
}
// Return first neighbor of "v"
int first(int v)
{
for (int i = 0; i < numVertex; i++)
if (matrix[v][i] != 0) return i;
return numVertex; // Return n if none
}
// Return v's next neighbor after w
int next(int v, int w)
{
for(int i = w + 1; i < numVertex; i++)
if (matrix[v][i] != 0)
return i;
return numVertex; // Return n if none
}
// Set edge (v1, v2) to "wt"
void setEdge(int v1, int v2, int wt)
{
// Assert(wt>0, "Illegal weight value");
assert(wt > 0);
if (matrix[v1][v2] == 0) numEdge++;
matrix[v1][v2] = wt;
}
void delEdge(int v1, int v2) // Delete edge (v1, v2)
{
if (matrix[v1][v2] != 0) numEdge--;
matrix[v1][v2] = 0;
}
bool isEdge(int i, int j) // Is (i, j) an edge?
{
return matrix[i][j] != 0;
}
int weight(int v1, int v2)
{
return matrix[v1][v2];
}
int getMark(int v)
{
return mark[v];
}
void setMark(int v, int val)
{
mark[v] = val;
}
int getInDegree(int v) // 求顶点v的入度
{
int result = 0;
//............... 在此行以下插入补充代码
for(int i=0;i<numVertex;i++)
{
if(isEdge(i,v)) result++;
}
//............... 在此行以上插入补充代码
return result;
}
int getOutDegree(int v) // 求顶点v的出度
{
int result = 0;
//............... 在此行以下插入补充代码
for(int i=0;i<numVertex;i++)
{
if(isEdge(v,i)) result++;
}
//............... 在此行以上插入补充代码
return result;
}
};
# endif // GRMATH
5.grlist.h(邻接表表示法)
// From the software distribution accompanying the textbook
// "A Practical Introduction to Data Structures and Algorithm Analysis,
// Third Edition (C++)" by Clifford A. Shaffer.
// Source code Copyright (C) 2007-2011 by Clifford A. Shaffer.
// Include this file to access Graph representation implemented using an
// Adjacency List
# ifndef GRLIST
# define GRLIST
#include <stdio.h>
#include <ctype.h>
#include <assert.h>
// Used by the mark array
#define UNVISITED 0
#define VISITED 1
#include "link.h"
#include "llist.h"
#include "graph.h"
// Edge class for Adjacency List graph representation
class Edge
{
int vert, wt;
public:
Edge()
{
vert = -1;
wt = -1;
}
Edge(int v, int w)
{
vert = v;
wt = w;
}
int vertex()
{
return vert;
}
int weight()
{
return wt;
}
};
class Graphl : public Graph
{
private:
List<Edge>** vertex; // List headers
int numVertex, numEdge; // Number of vertices, edges
int *mark; // Pointer to mark array
public:
Graphl(int numVert)
{
Init(numVert);
}
~Graphl() // Destructor
{
delete [] mark; // Return dynamically allocated memory
for (int i = 0; i < numVertex; i++) delete [] vertex[i];
delete [] vertex;
}
void Init(int n)
{
int i;
numVertex = n;
numEdge = 0;
mark = new int[n]; // Initialize mark array
for (i = 0; i < numVertex; i++) mark[i] = UNVISITED;
// Create and initialize adjacency lists
vertex = (List<Edge>**) new List<Edge>*[numVertex];
for (i = 0; i < numVertex; i++)
vertex[i] = new LList<Edge>();
}
int n()
{
return numVertex; // Number of vertices
}
int e()
{
return numEdge; // Number of edges
}
int first(int v) // Return first neighbor of "v"
{
if (vertex[v]->length() == 0)
return numVertex; // No neighbor
vertex[v]->moveToStart();
Edge it = vertex[v]->getValue();
return it.vertex();
}
// Get v's next neighbor after w
int next(int v, int w)
{
Edge it;
if (isEdge(v, w))
{
if ((vertex[v]->currPos() + 1) < vertex[v]->length())
{
vertex[v]->next();
it = vertex[v]->getValue();
return it.vertex();
}
}
return n(); // No neighbor
}
// Set edge (i, j) to "weight"
void setEdge(int i, int j, int weight)
{
// Assert(weight>0, "May not set weight to 0");
assert(weight > 0);
Edge currEdge(j, weight);
if (isEdge(i, j)) // Edge already exists in graph
{
vertex[i]->remove();
vertex[i]->insert(currEdge);
}
else // Keep neighbors sorted by vertex index
{
numEdge++;
for (vertex[i]->moveToStart();
vertex[i]->currPos() < vertex[i]->length();
vertex[i]->next())
{
Edge temp = vertex[i]->getValue();
if (temp.vertex() > j) break;
}
vertex[i]->insert(currEdge);
}
}
void delEdge(int i, int j) // Delete edge (i, j)
{
if (isEdge(i, j))
{
vertex[i]->remove();
numEdge--;
}
}
bool isEdge(int i, int j) // Is (i,j) an edge?
{
Edge it;
for (vertex[i]->moveToStart();
vertex[i]->currPos() < vertex[i]->length();
vertex[i]->next()) // Check whole list
{
Edge temp = vertex[i]->getValue();
if (temp.vertex() == j) return true;
}
return false;
}
int weight(int i, int j) // Return weight of (i, j)
{
Edge curr;
if (isEdge(i, j))
{
curr = vertex[i]->getValue();
return curr.weight();
}
else return 0;
}
int getMark(int v)
{
return mark[v];
}
void setMark(int v, int val)
{
mark[v] = val;
}
int getInDegree(int v) // 求顶点v的入度
{
int result = 0;
//............... 在此行以下插入补充代码
for(int i=0;i<numVertex;i++)
{
if(isEdge(i,v)) result++;
}
//............... 在此行以上插入补充代码
return result;
}
int getOutDegree(int v) // 求顶点v的出度
{
int result=0;
//............... 在此行以下插入补充代码
for(int i=0;i<numVertex;i++)
{
if(isEdge(v,i)) result++;
}
//............... 在此行以上插入补充代码
return result;
}
};
# endif // GRLIST
6.link.h
#include <iostream>
template <typename E> class Link {
public:
E element; // 结点值
Link *next; // 结点指针:在链表中指向下一结点
// 构造函数
Link(const E& elemval, Link* nextval =NULL)
{ element = elemval; next = nextval; }
Link(Link* nextval =NULL) { next = nextval; }
};
7.list.h
#ifndef LIST
#define LIST
template <typename E> class List { // List ADT
private:
void operator =(const List&) {} // Protect assignment
List(const List&) {} // Protect copy constructor
public:
List() {} // 默认构造函数
virtual ~List() {} // 基本的析构函数
// 从列表中清除内容,让它空着
virtual void clear() = 0;
// 在当前位置插入一个元素
// item: 要插入的元素
virtual void insert(const E& item) = 0;
// 在列表的最后添加一个元素
// item: 要添加的元素
virtual void append(const E& item) = 0;
// 删除和返回当前元素
// Return: 要删除的元素
virtual E remove() = 0;
// 将当前位置设置为列表的开始
virtual void moveToStart() = 0;
// 将当前位置设置为列表的末尾
virtual void moveToEnd() = 0;
// 将当前位置左移一步,如果当前位置在首位就不变
virtual void prev() = 0;
// 将当前位置右移一步,如果当前位置在末尾就不变
virtual void next() = 0;
// 返回列表当前元素个数
virtual int length() const = 0;
// 返回当前位置
virtual int currPos() const = 0;
// 设置当前位置
// pos: 要设置的当前位置
virtual void moveToPos(int pos) = 0;
// Return: 当前位置的元素
virtual const E& getValue() const = 0;
};
#endif
8.llist.h
#include "list.h"
#include<assert.h>
// This is the declaration for LList. It is split into two parts
// because it is too big to fit on one book page
// Linked list implementation
using namespace std;
template <typename E> class LList: public List<E> {
private:
Link<E>* head; // 指向链表头结点
Link<E>* tail; // 指向链表最后一个结点
Link<E>* curr; // 指向当前元素
int cnt; // 当前列表大小
void init() { // 初始化
curr = tail = head = new Link<E>;
cnt = 0;
}
void removeall() { // Return link nodes to free store
while(head != NULL) {
curr = head;
head = head->next;
delete curr;
}
}
public:
LList(int size=100) { init(); } // 构造函数
~LList() { removeall(); } // 析构函数
void print() const; // 打印列表内容
void clear() { removeall(); init(); } // 清空列表
// 在当前位置插入“it”
void insert(const E& it) {
curr->next = new Link<E>(it, curr->next);
if (tail == curr) tail = curr->next; //新的尾指针
cnt++;
}
void append(const E& it) { // 追加“it”到列表中
tail = tail->next = new Link<E>(it, NULL);
cnt++;
}
// 删除并返回当前元素
E remove() {
assert(curr->next != NULL);//"No element"
E it = curr->next->element; // 保存元素值
Link<E>* ltemp = curr->next; // 保存指针域信息
if (tail == curr->next) tail = curr; // 重置尾指针
curr->next = curr->next->next; // 从列表中删除
delete ltemp; //回收空间
cnt--; // 当前列表大小减一
return it;
}
void moveToStart() // 将curr设置在列表头部
{ curr = head; }
void moveToEnd() // 将curr设置在列表尾部
{ curr = tail; }
// 将curr指针往前(左)移一步;如果已经指向头部了就不需要改变
void prev() {
if (curr == head) return; // 之前没有元素
Link<E>* temp = head;
// March down list until we find the previous element
while (temp->next!=curr) temp=temp->next;
curr = temp;
}
// 将curr指针往后(右)移一步;如果已经指向尾部了就不需要改变
void next()
{ if (curr != tail) curr = curr->next; }
int length() const { return cnt; } // 返回当前列表大小
// 返回当前元素的位置
int currPos() const {
Link<E>* temp = head;
int i;
for (i=0; curr != temp; i++)
temp = temp->next;
return i;
}
// 向下移动到列表“pos”位置
void moveToPos(int pos) {
assert ((pos>=0)&&(pos<=cnt));//"Position out of range"
curr = head;
for(int i=0; i<pos; i++) curr = curr->next;
}
const E& getValue() const { // 返回当前元素
assert(curr->next != NULL);//"No value"
return curr->next->element;
}
};