学习来源:计蒜客

1.什么是图
 
比如说我们的关系网络,就来微信来说,我们把用户看成是一个点,是好友的我们就把这两个点连起来。
如下面的就是一个简单的图
重学数据结构系列之——图的储存_链表
 

2.图的分类

 
有向图和无向图,上图就是无向图,有向图是单向的,边用箭头来表示,如下:
重学数据结构系列之——图的储存_有向图_02
 

3.图的常用概念

 
顶点:就是那些点咯
边:就是连接两个顶点的线,分为有向边和无线边
 
下面一般是指有向图
入度:该顶点有多少个箭头指着
出度:该顶点有多少条边箭头指着别人
顶点的度为入度与出度之和
 

3.图一般的储存方式

 
邻接矩阵和邻接表
 

4.邻接矩阵储存的实现

 
其实就是用一个二维数组来储存,比如说是二维数组mat,mat[1][2]=1 ,就说明编号为1,2的两个顶点有边(这里编号从0开始)
#include <iostream>
#include <cstring>
using namespace std;

class Graph{
private:
	int **mat;	//邻接矩阵,其实就是二维数组
	int n;	//顶点个数
public:
	Graph(int input_n){
		n= input_n;
		//先分配n个int*的指针,再对每个指针再循环分配
		mat = new int*[n];
		for (int i = 0; i < n; i++) {
			mat[i] = new int[n];
			//将内存中mat[i]的sizeof(int)*n个字节的内容全部设置为那个字符的ASCII值(这里的字符好似0)
			//它是对较大的结构体或数组进行清零操作的一种最快方法
			memset(mat[i], 0, sizeof(int)*n);//Sets buffers to a specified character.
		}
	}
	~Graph(){
		//析构时注意先析构里面的,再外层的
		for (int i = 0; i < n; i++) {
			delete[] mat[i];
		}
		delete[] mat;
	}
	//插入边,直接将对于的位置置为1就可以了
	void insert(int x, int y){
		mat[x][y] = 1;
	}
	//输出邻接矩阵
	void output(){
		for (int i = 0; i < n; i++) {
			for (int j = 0; j < n; j++) {
				cout<<mat[i][j]<<" ";
			}
			cout<<endl;
		}
	}
};

int main(){
	//n:顶点个数	m:有向边个数
	//x,y: 表示插入一条x连向y的有向边
	int n, m, x, y;
	cin >> n >> m;
	Graph g(n);
	for (int i = 0; i< m; i++) {
		cin >> x >> y;
		g.insert(x, y);
	}
	g.output();
	return 0;
}
 
对memset的理解: 通过调试,可以看到内存的地址的值全都变成了00000000,因为int是四个字节嘛,那int指针也是。
重学数据结构系列之——图的储存_链表_03

运行结果:

重学数据结构系列之——图的储存_有向图_04
 

5.邻接表的储存实现

#include <iostream>
using namespace std;

//链表结点
class LinkedListNode{
public:
	//vertex:链表结点的值
	int vertex;
	//指向下一个结点的指针
	LinkedListNode *next;
	LinkedListNode(int vertex_input){
		vertex = vertex_input;
		next = NULL;
	}
};

//链表
class LinkedList{
public:
	LinkedListNode *head;

	LinkedList(){
		head = NULL;
	}

	~LinkedList(){
		//顺着链析构
		while (head != NULL) {
			LinkedListNode *delete_node = head;
			head = head->next;
			delete delete_node;
		}
	}
	//插入函数,这里直接插到头的前面了
	void insert(int vertex){
		LinkedListNode *node = new LinkedListNode(vertex);
		node->next = head;
		head = node;
	}
};

//图
class Graph{
private:
	//储存链表的指针
	LinkedList *edges;
	//n:顶点数目
	int n;
public:
	Graph(int input_n){
		n = input_n;
		edges = new LinkedList[n];
	}
	~Graph(){
		delete[] edges;
	}
	
	//插入就调用链表的插入
	void insert(int x, int y) {
		edges[x].insert(y);
    }
	
	//循环输出每个链表的每个结点的值即可
    void output() {
		for (int i = 0; i < n; i++) {
			cout<<i<<":";
			//auto 是 C++11 的新特性,在定义变量时无需指定类型,编译器会通过类型推导得到实际的类型
			//但我用vc++6.0,所以不支持
			//for (auto* j = edges[i].head; j != NULL; j = j->next) {
			for (LinkedListNode* j = edges[i].head; j != NULL; j = j->next) {
				cout<<j->vertex<<" ";
			}
			cout<<endl;
		}
    }
};

int main(){
	int n, m, x, y;
    cin >> n >> m;
    Graph g(n);
    for (int i = 0; i < m; ++i) {
        cin >> x >> y;
        g.insert(x, y);
    }
    g.output();
	return 0;
}

运行结果:(可以看到结果非常直观,那个顶点有没有指向谁的边都一眼看出来)

重学数据结构系列之——图的储存_i++_05
 
 
 
 
 
 

 

网络上志同道合,我们一起学习网络安全,一起进步,QQ群:694839022