数据结构代码整理_基于邻接表存储结构的有向图的实现(C++)

Requirements

       基于邻接表存储结构实现有向图的典型操作(构造、析构、增加顶点、删除顶点、增加弧、删除弧,查找一个 顶点、判空、判满、图中顶点个数、邻接表中指定顶点的第一个邻接顶点、深度优先遍历、广度优先遍历),测试和调试程序。

Debugging Environment

visual studio 2019

Chat

之前的图论问题中对于图的存储基本上都是用的链式向前星,刚好趁着这次课程实验了解一下邻接表,可能是第一次接触邻接表还不是很熟悉的缘故吧,总觉得邻接表好像比链式向前星麻烦一点。因为只是随手写的,可能会有一些小bug,在graph.h后面还有个test.cpp文件,那个是老师是给的测试文档,我跑了一下都还ok,但是你们在使用的时候如果发现了什么不对的地方,记得评论提醒我哈。

Code

(代码仅供参考)

1.graph.h

#ifndef GRAPH_H_INCLUDED
#define GRAPH_H_INCLUDED
/* This header file contains the declarations and prototype functions for the graph ADT
class. It also contains a debugging function to print a vertex.
*/
#include<iostream>
#include<map>
#include<queue>
#include<stack>
using namespace std;
#define DEBUG 1 //On includes printVertex
template <class TYPE> struct Vertex;//存储每个顶点的信息
template <class TYPE> struct Arc;//先简单声明,存储弧的信息
template <class TYPE> struct Vertex //存图中各顶点
{
	Vertex<TYPE>* pNextVertex;//指向下一个头顶点
	TYPE data;//顶点有效信息
	int inDegree;//入度
	int outDegree;//出度
	short processed;//标识该顶点被处理的程度或状态
	Arc<TYPE>* pArc; //指向第一条弧,这里用到了 Arc,所以在前面要先简单声明下
}; // Vertex
template <class TYPE> struct Arc //存图中各弧
{
	Vertex<TYPE>* destination;//注意这里是指针,指向弧头顶点
	Arc<TYPE>* pNextArc;//指向下一条弧
}; // Arc
template <class TYPE, class KTYPE> //KTYPE 表示各顶点中关键字的类型(关键字用于查询)
class Graph
{
private:
	int count;
	Vertex<TYPE>* first;
public:
	Graph(void);
	~Graph(void);
	int insertVertex(TYPE dataIn);//插入顶点,数据域为 dataIn
	int deleteVertex(KTYPE dltKey);//删除顶点,其数据域中关键字为 dltKey
	int insertArc(KTYPE fromKey, KTYPE toKey);//插入弧,该弧从 fromKey 指向 toKey
	int deleteArc(KTYPE fromKey, KTYPE toKey);//删除弧,该弧从 fromKey 指向 toKey
	int retrieveVertex(KTYPE key, TYPE& DataOut);//查关键字为 key 的顶点,数据存入 DataOut
	int firstArc(KTYPE key, TYPE& DataOut);//查关键字为 key 的顶点所指的首弧,数据存 DataOut
	bool emptyGraph(void);//判图是否为空
	bool graphFull(void);//判图是否为满
	int graphCount(void); //返回图顶点个数
	void depthFirst(void (*process)(TYPE dataProc));//深度优先遍历
	void breadthFirst(void (*process)(TYPE dataProc)); // 广度优先遍历
#ifdef DEBUG
		// The following function is used only for debugging
		void printVertex(KTYPE key);//该方法仅在调试时使用,输出该顶点关键字
#endif
}; // Graph
template <class TYPE, class KTYPE>
Graph<TYPE, KTYPE>::Graph()
{
	count = 0;
	first = nullptr;
}
template <class TYPE, class KTYPE>
Graph<TYPE, KTYPE>::~Graph(void)
{
	Vertex<TYPE>* p = first;
	while (p) {
		Vertex<TYPE>* q = p;
		p = p->pNextVertex;
		delete q;
	}
}
template <class TYPE, class KTYPE>
int Graph<TYPE, KTYPE>::insertVertex(TYPE dataIn)//插入顶点,数据域为 dataIn
{
	Vertex<TYPE>* p = new Vertex<TYPE>;
	p->data = dataIn;
	p->inDegree = 0;
	p->outDegree = 0;
	p->pArc = nullptr;
	p->pNextVertex = nullptr;
	count++;
	Vertex<TYPE>* temp = first;
	if (!first)
		first = p;
	else {
		while (temp->pNextVertex) {
			temp = temp->pNextVertex;
		}
		temp->pNextVertex = p;
	}
	return 1;
}
template <class TYPE, class KTYPE>
int Graph<TYPE, KTYPE>::deleteVertex(KTYPE dltKey)//删除顶点,其数据域中关键字为 dltKey
{
	Vertex<TYPE>* p = first, * q = nullptr;
	while (p) {
		if (p->data.key == dltKey) {//删除节点表中的节点
			if (p->inDegree||p->outDegree)
				return -1;
			Vertex<TYPE>* temp = p;
			p = p->pNextVertex;
			if (temp == first)//头删
				first = p;
			else
				q->pNextVertex = p;
			delete temp;
			count--;
			return 1;
		}
		q = p;
		p = p->pNextVertex;
	}
	return -2;
}
template <class TYPE, class KTYPE>
int Graph<TYPE, KTYPE>::insertArc(KTYPE fromKey, KTYPE toKey)//插入弧,该弧从 fromKey 指向 toKey
{
	int flag1 = 0, flag2 = 0;
	Vertex<TYPE>* p = first, * q1 = nullptr, * q2 = nullptr;
	while (p) {
		if (p->data.key == fromKey) {
			q1 = p;
			++flag1;
		}
		if (p->data.key == toKey) {
			q2 = p;
			++flag2;
		}
		p = p->pNextVertex;
	}
	if (!flag1)
		return -2;
	if (!flag2)
		return -3;
	Arc<TYPE>* a = new Arc<TYPE>;
	a->destination = q2;
	a->pNextArc = nullptr;
	Arc<TYPE>* temp = q1->pArc;
	if (!temp)
		q1->pArc = a;
	else {
		while (temp->pNextArc)
			temp = temp->pNextArc;
		temp->pNextArc = a;
	}
	a->destination->inDegree += 1;
	q1->outDegree += 1;
	return 1;
}
template <class TYPE, class KTYPE>
int Graph<TYPE, KTYPE>::deleteArc(KTYPE fromKey, KTYPE toKey)//删除弧,该弧从 fromKey 指向 toKey
{
	int flag1 = 0, flag2 = 0;
	Vertex<TYPE>* p = first, * q1 = nullptr;
	while (p) {
		if (p->data.key == fromKey) {
			q1 = p;
			++flag1;
		}
		p = p->pNextVertex;
	}
	if (!flag1)
		return -2;
	Arc<TYPE>* q2 = q1->pArc, * temp = nullptr;
	while (q2) {
		if (q2->destination->data.key == toKey)
		{
			if (temp)
				temp->pNextArc = q2->pNextArc;
			else
				q1->pArc = q2->pNextArc;
			q2->destination->inDegree -= 1;
			q1->outDegree -= 1;
			delete q2;
			return 1;
		}
		temp = q2;
		q2 = q2->pNextArc;
	}
	return -3;
}
template <class TYPE, class KTYPE>
int Graph<TYPE, KTYPE>::retrieveVertex(KTYPE key, TYPE& DataOut)//查关键字为 key 的顶点,数据存入 DataOut
{
	Vertex<TYPE>* p = first;
	while(p){
		if (p->data.key == key) {
			DataOut = p->data;
			return 1;
		}
		p = p->pNextVertex;
	}
	return 0;
}
template <class TYPE, class KTYPE>
int Graph<TYPE, KTYPE>::firstArc(KTYPE key, TYPE& DataOut)//查关键字为 key 的顶点所指的首弧,数据存 DataOut
{
	Vertex<TYPE>* p = first;
	while (p) {
		if (p->data.key == key) {
			if (p->pArc) {
				DataOut = p->pArc->destination->data;
				return 1;
			}
			return -1;
		}
		p = p->pNextVertex;
	}
	return -2;
}	
template <class TYPE, class KTYPE>
bool Graph<TYPE, KTYPE>::emptyGraph(void)//判图是否为空
{
	return count == 0;
}
template <class TYPE, class KTYPE>
bool Graph<TYPE, KTYPE>::graphFull(void)//判图是否为满
{
	return false;
}
template <class TYPE, class KTYPE>
int Graph<TYPE, KTYPE>::graphCount(void) //返回图顶点个数
{
	return count;
}
template <class TYPE, class KTYPE>
void Graph<TYPE, KTYPE>::depthFirst(void (*process)(TYPE dataProc))//深度优先遍历
{
	if (first) {
		Vertex<TYPE>* p = first;
		int cnt = 0;
		stack<Arc<TYPE>* >st;
		while (p) {//标记置位
			p->processed = 0;
			p = p->pNextVertex;
		}
		p = first;
		while (!st.empty() || cnt == 0) {
			Vertex<TYPE>* ptemp = first;
			if (cnt != 0) {
				ptemp = st.top()->destination;
				st.pop();
			}
			process(ptemp->data);
			Arc<TYPE>* temp = ptemp->pArc;
			while (temp) {
				if (!temp->destination->processed) { //遇到当前弧表的未处理弧
					st.push(temp);
					temp->destination->processed = 1;
				}
				temp = temp->pNextArc;
			}
			cnt++;
		}
	}
}
template <class TYPE, class KTYPE>
void Graph<TYPE, KTYPE>::breadthFirst(void (*process)(TYPE dataProc)) // 广度优先遍历
{
	if (first) {
		Vertex<TYPE>* p = first;
		Arc<TYPE>* a = p->pArc;
		queue<Vertex<TYPE>* > q;
		while (p) {//初始化
			p->processed = 0;
			p = p->pNextVertex;
		}
		if (first)
			q.push(first);
		while (!q.empty()) {
			Vertex<TYPE>* ptemp = q.front();
			q.pop();
			if (!ptemp->processed) {
				process(ptemp->data);
				ptemp->processed = 1;
			}
			Arc<TYPE>* temp = ptemp->pArc;
			while (temp) {
				if (!temp->destination->processed)
				{
					process(temp->destination->data);
					temp->destination->processed = 1;
					q.push(temp->destination);
				}
				temp = temp->pNextArc;
			}
		}
	}
}
template <class TYPE, class KTYPE>
void Graph<TYPE, KTYPE>::printVertex(KTYPE key)//该方法仅在调试时使用,输出该顶点关键字
{
	Vertex<TYPE>* p = first;
	while (p) {
		if (p->data.key == key) {
			cout << "Adjacency list for " << key;
			cout << " " << "inDegree " << p->inDegree << "--outDegree " << p->outDegree << endl;
			Arc<TYPE>* temp = p->pArc;
			while (temp) {
				cout << " " << temp->destination->data.key;
				temp = temp->pNextArc;
			}
			break;
		}
		p = p->pNextVertex;
	}
}

#endif // GRAPH_H_INCLUDED

2.test.cpp

/* This program tests the graph ADT */
#include <iostream>
#include <iomanip>
#include <ctype.h>
#include <stdlib.h>
#include <limits.h>
#include "graph.h"
using namespace std;
struct DATA
{
	int key;
	bool operator<(const DATA& p)const {
		return key < p.key;
	}
};
//Prototype Functions
void doInsertVertex(Graph<DATA, int>& graph);
void doInsertArc(Graph<DATA, int>& graph);
void doDeleteVertex(Graph<DATA, int>& graph);
void doDeleteArc(Graph<DATA, int>& graph);
void search(Graph<DATA, int>& graph);
void doRetrieveVertex(Graph<DATA, int>& graph);
void doFirstArc(Graph<DATA, int>& graph);
void testUtilties(Graph<DATA, int>& graph);
void buildAGraph(Graph<DATA, int>& graph);
char doMenu(void);
void process(DATA data);
int main(void)
{ // Local Declarations
	char option;
	int key;
	DATA datum;
	Graph<DATA, int> graph;
	// Statements
	cout << "Start Explore Graphs\n";
	while ((option = doMenu()) != 'q')
	{
		switch (option)
		{
		case 'i': doInsertVertex(graph); break;
		case 'd': doDeleteVertex(graph); break;
		case 'a': doInsertArc(graph); break;
		case 'b': doDeleteArc(graph); break;
		case 'p': cout << "Enter vertex key: ";
			cin >> key;
			graph.printVertex(key);
			break;
		case 'r': doRetrieveVertex(graph); break;
		case 's': doFirstArc(graph); break;
		case 't': datum.key = INT_MIN;
				process(datum);//Set count
			cout << "\nBegin depth first traversal\n";
			graph.depthFirst(process);
			cout << "\nEnd depth first traversal\n";
			cout << "\nBegin breadth first traversal\n";
			process(datum); //Set count
			graph.breadthFirst(process);
			cout << "\nEnd breadth first traversal\n";
			break;
		case 'x': testUtilties(graph); break;
		case '!': buildAGraph(graph); break;
		default: cout << "\a\a\nInvalid option. Please try again.\n";
		} // switch
	} // while
	return 0;
	cout << "End Explore Graphs\n";
} // main
/* ============================= doMenu =============================
This function prints a menu and reads the user selection.
Pre Nothing.
Post option returned.
*/
char doMenu(void)
{ // Local Declarations
	char option;
	// Statements
	cout << "\n============ Menu =============\n";
	cout << " i : Insert new item \n";
	cout << " d : Delete a vertex \n";
	cout << " a : Insert an arc \n";
	cout << " b : Delete an arc \n";
	cout << " p : Print Vertex Adjacency \n";
	cout << " r : Retrieve Vertex \n";
	cout << " s : Retrieve First Arc \n";
	cout << " t : Traverse graph \n";
	cout << " x : Test ADT utilities \n";
	cout << " q : Quit \n\n";
	cout << "=============================== \n";
	cout << "Please enter your choice: ";
	cin >> option;
	option = tolower(option);
	return option;
} // doMenu
/* =================== doRetrieveVertex ===================
This function locates a vertex in the graph.
Pre graph exists.
Post vertex retrieved and printed.
*/
void doRetrieveVertex(Graph<DATA, int>& graph)
{ // Local Declarations
	int key;
	int success;
	DATA dataOut;
		// Statements
		cout << "Please enter key to be found: ";
	cin >> key;
	success = graph.retrieveVertex(key, dataOut);
	if (success == 1)
		cout << "\nKey found. Contains " << dataOut.key << endl;
	else
		cout << "\n\aKey " << key << " not found\n";
	return;
} // doRetrieveVertex
/* =================== doFirstArc ===================
This function locates the first arc of a vertex.
Pre graph exists.
Post vertex first arc retrieved and printed.
*/
void doFirstArc(Graph<DATA, int>& graph)
{ // Local Declarations
	int key;
	int success;
	DATA dataOut;
	// Statements
	cout << "Please enter key of first arc: ";
	cin >> key;
	success = graph.firstArc(key, dataOut);
	if (success == 1)
		cout << "\nFirst arc of " << key << " contains " << dataOut.key;
	else if (success == -2)
		cout << "\n\aVertex Key " << key << " not found\n";
	else
		cout << key << " has no arcs\n";
	return;
} // doFirstArc
/* ===================== doInsertVertex =====================
This function gets input from the user and passes it to
vertex insert vertex function.
Pre graph exists.
Post vertex has been inserted.
*/
void doInsertVertex(Graph<DATA, int>& graph)
{ // Prototype Statements
// Local Declarations
	DATA newData;
	int success;
	// Statements
	cout << "\nPlease enter interger to be inserted: ";
	cin >> newData.key;
	success = graph.insertVertex(newData);
	if (success)
		cout << newData.key << " inserted\n";
	else
		cout << newData.key << " NOT inserted\a\n";
	return;
} // doInsertVertex
/* ======================= doInsertArc =======================
This function gets input from the user and passes it to
arc insert vertex function.
Pre graph exists.
Post arc has been inserted.
*/
void doInsertArc(Graph<DATA, int>& graph)
{
	// Prototype Statements
	// Local Declarations
	int fromKey;
	int toKey;
	int success;
	// Statements
	cout << "\nPlease enter from key: ";
	cin >> fromKey;
	cout << "\nPlease enter to key: ";
	cin >> toKey;
	success = graph.insertArc(fromKey, toKey);
	switch (success)
	{
	case 1: cout << "arc " << fromKey << "-to-" << toKey << " inserted\n";
		break;
	case -1: cout << "Overflow: arc NOT inserted\a\n";
		break;
	case -2: cout << "From Key " << fromKey << " Error\a\n";
		break;
	case -3: cout << "To Key " << toKey << " Error\a\n";
		break;
	default: cout << "Unknown error " << success << " in doInsertArc\n";
		cout << "ABORTING 100\a\a\n";
		exit(100);
	} // switch
	return;
} // doInsertArc
/* ======================= doDeleteArc =======================
This function gets input from the user and passes it to
the delete arc function.
Pre graph exists.
Post arc has been deleted.
*/
void doDeleteArc(Graph<DATA, int>& graph)
{ // Local Declarations
	int fromKey;
	int toKey;
	int success;
	// Statements
	cout << "\nPlease enter from key: ";
	cin >> fromKey;
	cout << "\nPlease enter to key: ";
	cin >> toKey;
		success = graph.deleteArc(fromKey, toKey);
	switch (success)
	{
	case 1: cout << "arc " << fromKey << "-to-" << toKey << " deleted\n";
		break;
	case -2: cout << "From Key " << fromKey << " Error\a\n";
		break;
	case -3: cout << "To Key " << toKey << " Error\a\n";
		break;
	default: cout << "Unknown error " << success << "in doDeleteArc\n";
		cout << "ABORTING 101\a\a\n";
		exit(101);
	} // switch
	return;
} // doDeleteArc
/* ========================== doDeleteVertex ==========================
This function deletes a node from the graph. It asks the user for the
key of the node to be deleted and then removes it from the graph.
Pre graph must be initialized. Null graph is OK.
Post The node is deleted and its space recylced
-or- An error message is printed.
*/
void doDeleteVertex(Graph<DATA, int>& graph)
{ // Prototype Statements
// Local Declarations
	int dltKey;
	int success;
	DATA dltData;
	// Statements
	cout << "\nPlease enter integer to be deleted: ";
	cin >> dltKey;
	graph.retrieveVertex(dltKey, dltData);//即将被删的元素读出来到 dltDat 中
	success = graph.deleteVertex(dltKey);//真正删除被删的元素
	switch (success)
	{
	case 1: cout << dltKey << " deleted\n";
		break;
	case -1: cout << dltKey << "'s degree not zero\a\n";
		break;
	case -2: cout << dltKey << "'s key not found\a\n";
		break;
	default: cout << "UNKNOWN ERROR 101 in delete\a\n";
		cout << "ABORTING\a";
		exit(101);
	} // switch
	return;
} // doDeleteVertex
/* =================== process ==================
This function "processes" a graph by printing a
sequential number and the vertex data.
Pre dataPtr is pointer to user data structure
Post Data (integer) printed.
*/
void process(DATA data)
{ // Local Declarations
	static int count = 1;
		// Statements
		if (data.key == INT_MIN)
			count = 1;
		else
			cout << "Vertex #" << setw(3) << count++
			<< " contains: " << setw(3) << data.key << endl;
	return;
} // process
/* =================== testUtilties ==================
This function tests the ADT utilities by calling
each in turn and printing their results.
Pre graph has been created.
Post Results printed.
*/
void testUtilties(Graph<DATA, int>& graph)
{
	cout << "Graph contains " << graph.graphCount() << " nodes\n";
	if (graph.emptyGraph())
		cout << "The graph IS empty\n";
	else
		cout << "The graph IS NOT empty\n";
	if (graph.graphFull())
		cout << "The graph IS full\a\n";
	else
		cout << "The graph IS NOT full\n";
	return;
} // testUtilities
/* =================== buildAGraph ==================
This function builds the graph in Figure 12-1(a)
using 1 for A, 2 for B, etc.
Pre graph has been created.
Post graph built.
*/
void buildAGraph(Graph<DATA, int>& graph)
{
	// Local Declarations
	DATA data;
	int i;
	int success;
	int fromKey;
	int toKey;
	// Statements
	for (i = 1; i <= 6; i++)
	{
		data.key = i;
		success = graph.insertVertex(data);
		if (!success)
			cout << "Insert error in buildAGraph\a\a\n", exit(100);
	} // for
	// Vertices built. Now build arcs
	fromKey = 1;
	toKey = 2;
	success = graph.insertArc(fromKey, toKey);
	fromKey = 2;
	toKey = 3;
	success = graph.insertArc(fromKey, toKey);
	fromKey = 3;
	toKey = 4;
	success = graph.insertArc(fromKey, toKey);
	fromKey = 3;
	toKey = 5;
	success = graph.insertArc(fromKey, toKey);
	fromKey = 5;
	toKey = 4;
	success = graph.insertArc(fromKey, toKey);
	fromKey = 5;
	toKey = 6;
	success = graph.insertArc(fromKey, toKey);
	fromKey = 2;
	toKey = 5;
	success = graph.insertArc(fromKey, toKey);
	if (!success)
		cout << "Error adding arcs in buildAGraph\a\a\n", exit(100);
	return;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

chaoql

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值