(九)数据结构之邻接矩阵、邻接表存储图

1,邻接矩阵简述

 邻接矩阵:采用二维数组的形式来存储图或网。
有向、无向图,有向、无向网都可以采用邻接矩阵的方式存储,下面分别分析一些需要注意的细节点:
1,有向图:有向图的邻接矩阵根据主对角线对称
2,无向图:根据主对角线对称(因为对称,所以我们只需填充上半三角(或下半三角)即可,但这样也会浪费很多的存储空间,所以此时若我们再利用矩阵压缩的办法来压缩存储为一维数组,那么这一弊端将会被降到最小)
3,有向网:与有向图类似,但是每条边都被赋有权重值
4,无向网:与无向图类似,但是每条边都被赋有权重值(也可压缩存储节约内存)

1.1,邻接矩阵适用情况

 从总体上来看,邻接矩阵适合于稠密图

设边数为e,节点数为n,那么
       当 e >> 1 / 2 * n * (n - 1)
时,用邻接矩阵比邻接表更节省空间

2,邻接表简述

 邻接表的特点就是用表头存储所有的图节点,在存储有向图时,在每个表头结点后都接上以它为弧尾的弧的弧首节点(有的拗口)。这样我们能够很容易的得到一个顶点的出度(即相对应的那个表头结点链表的链表节点个数),而且更好的是我们将邻接表转为逆邻接表是非常方便的,这意味着我们会很容易知道每个节点的入度!
缺点:邻接表的缺陷呢,就是如果你想知道节点i和节点j之间是否直接相连时你需要去到表头节点i和j去分别遍历该条链表。这和邻接矩阵来比显然是麻烦许多的。
所以要合理选择存储结构。

2.1,邻接表适用情况

 从总体上来看,邻接表适合于稀疏图

设边数为e,节点数为n,那么
       当 e << 1 / 2 * n * (n - 1)
时,用邻接表比邻接矩阵更节省空间

3,邻接矩阵实现代码

看代码,

#include <iostream>
using namespace std;
//-----------------------------------------图的邻接矩阵存储结构实现--------------------------------// 
#define MAXNUM 20 //最大节点个数

//图的种类类:有向图,有向网,无向图,无向网
typedef enum {
	DG,		//有向图
	DN,		//有向网
	UDG,	//无向图
	UDN		//无向网
} Graph_type;
//图类 
class Graph {
	public:
		Graph(int node, int edge, Graph_type type) : node_num(node), edge_num(edge), kind(type) {
			for (int i = 0; i < node_num; ++i)
				for (int j = 0; j < node_num; ++j)
					matrix[i][j] = 0; //初始化 
		}
		int CreateMatrix();
		void display() const;
		Graph_type kind;
		int edge_num,
			node_num;
		int matrix[MAXNUM][MAXNUM]; //邻接矩阵 
};
//建立邻接矩阵
int Graph::CreateMatrix() {
	cout << "输入两个顶点序号以及一个1表示有边(网需要输入权重值,可为1)" << endl;
	
	int i; int tmpNode1, tmpNode2, weight;
	
	for (i = 0; i < edge_num; ++i) {
		cout << "输入第" << i + 1 << "条边的信息: (" << edge_num - i << "条待输入) ";
		cin >> tmpNode1 >> tmpNode2 >> weight; //不考虑非法输入
		--tmpNode1, --tmpNode2;
		matrix[tmpNode1][tmpNode2] = (kind == DG || kind == UDG) ? 1 : weight;
		matrix[tmpNode2][tmpNode1] = (kind == UDG || kind == UDN) ? weight : 0;
	}
	
	cout << "输入成功" << endl;
}
//输出邻接矩阵 
void Graph::display() const {
	for (int i = 0; i < node_num; ++i) {for (int j = 0; j < node_num; ++j) cout << matrix[i][j] << " ";  cout << endl;}
					
	system("pause");
}
int main() {
	
    cout << "请输入节点数,边数,以及图的种类(0, 1, 2, 3来表示): " << endl;
    int node, edge, type_; Graph_type type;
    
    cin >> node >> edge >> type_; //忽略非法输入
    type_ == 0 ? type = DG : (type_ == 1 ? type = DN : (type_ == 2 ? type = UDG : type = UDN));
	
	Graph G(node, edge, type); //建图
	
	G.CreateMatrix(); //建立邻接矩阵
	
	G.display(); //输出邻接矩阵 
	
    return 0;
}

3.1,运行截图

邻接矩阵运行截图
可以看到非常完美的实现了我们需要的功能

4,邻接表实现代码

看代码,

#include <iostream>
using namespace std;
//---------------------------------------图的邻接表存储结构的建立----------------------------------//
#define MAXNUM 20 //最大节点个数 
typedef struct Vertex {
	//elemtype data; 节点的数据域这里我就省略了,只建立邻接表 
	int num; //节点编号
	struct Vertex *next;
	Vertex(int x = 0, struct Vertex *ptr = nullptr) : num(x), next(ptr) {}
} Vertex, *p_Vertex;

typedef struct TableNode { //表头
	Vertex *next;
	TableNode() {next = nullptr;}
} TableNode, TableHead[MAXNUM];

class Graph { //有向图
	public:
		Graph(int node, int edge) : nodeNum(node), edgeNum(edge) {}
		int CreateTable(); //邻接表 
		int CreateTable_reverse(); //逆邻接表 
		void displayTable(int) const; //打印邻接表 
	private:
		TableHead num; //邻接表头
		TableHead numReverse; //逆邻接表头 
		int nodeNum,
			edgeNum;
};
int Graph::CreateTable() {
	cout << "请输入弧尾节点和弧头节点(注意顺序)" << endl; //省略非法输入检查
	
	int node1, node2; p_Vertex ptr; bool mark = false;
	for (int i = 0; i < edgeNum; ++i) {
		cin >> node1 >> node2;
		
		num[node1 - 1].next ? ptr = num[node1 - 1].next, mark = true : (num[node1 - 1].next = new Vertex(node2 - 1), mark = false);
		
		if (mark) {while(ptr->next) ptr = ptr->next; ptr->next = new Vertex(node2 - 1);}
	}
	return 0; //ok
}
void Graph::displayTable(int m) const {
	p_Vertex ptr; 
	
	if (m) cout << "建成的邻接表为: " << endl; else cout << "建成的逆邻接表为: " << endl;
	
	if (m == 1)for (int i = 0; i < nodeNum; ++i) {ptr = num[i].next; cout << i << " -> "; while(ptr) {cout << ptr->num << " -> "; ptr = ptr->next;} cout << "null" << endl;}
	else       for (int i = 0; i < nodeNum; ++i) {ptr = numReverse[i].next; cout << i << " -> "; while(ptr) {cout << ptr->num << " -> "; ptr = ptr->next;} cout << "null" << endl;}
}
int Graph::CreateTable_reverse() {
	//邻接表改逆邻接表
	p_Vertex p1, p2; bool mark = false;
	for (int i = 0; i < edgeNum; ++i) {
		p1 = num[i].next;
	 	while(p1) {
	 		(p2 = numReverse[p1->num].next) ? mark = true : (numReverse[p1->num].next = new Vertex(i), 1);
	 		if (mark) {while(p2->next) p2 = p2->next; mark = false; p2->next = new Vertex(i);}
	 		p1 = p1->next;
		}
	 }
	 return 0; //ok
}
int main()
{
	int node, edge; cout << "请输入图节点数和边的数目" << endl;
	
	cin >> node >> edge;
	
	Graph t(node, edge); t.CreateTable(); t.displayTable(1); cout << endl; //邻接表
	
	t.CreateTable_reverse(); t.displayTable(0); //逆邻接表
	
	return 0;
}

4.1,运行截图

邻接表运行截图
同时建成了邻接表和逆邻接表,完美运行!

5,ending

说下我的运行环境:IDE: devc++ ;Standard: c++11;Compiler:MinGW
重心慢慢转向devc++了,这其实是一个很好用的ide,vscode莫名编译缓慢??
老规矩,对你有帮助的话不要吝惜你的赞喔~~peace!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值