一、什么是图、为什么要使用图
图是由顶点和边构成的一种数据结构(边和顶点都存储着数据)。
使用图当然是为了解决编程问题,用广度优先搜索或者深度优先算法来解决。
1.有向图
如图1
2.无向图
如图2
3.连通图
任意两个顶点都有路径,比如图2中,虽然A、C没有直接连通,但是可以A>B>C,A、C间接连通。
4.完全图
如果把图2中A、C,B、D连接起来,就能叫做完全图。注意,就算图1中A、C,B、D连接起来,也不能叫完全图,因为有向图的完全图不仅要有A>B的路径,还要有B>A的路径。
二、图的存储
图1 有向图 图2 无向图
1.邻接矩阵(把顶点和边分开存储)
邻接矩阵的顶点(和哈希表类似)
//A:1 B:2 C:3 D:4
struct Node
{
int val; //顶点数据
Node(int x):val(x){}
};
Node*A = Node(1);
Node*B = Node(2);
Node*C = Node(3);
Node*D = Node(4);
邻接矩阵的边
//A:1 B:2 C:3 D:4
struct Map
{
vector<Node*>v1; //存放顶点
vector<vector<int>>matrix; //存放邻接矩阵
}
//假设存储图1
Map*map;
map->v1.push_back(A);
map->v1.push_back(B);
map->v1.push_back(C);
map->v1.push_back(D);
map->matrix = {{0,1,0,1},
{0,0,1,0},
{0,0,0,0},
{0,0,1,0}};
//假如把二维数组中的1换成我们想要存储的数据,是不是就意味着我们的边也可以存储数据
2.邻接表
邻接表的顶点
//A:1 B:2 C:3 D:4
struct Node
{
int val; //顶点数据
Arc* head; //顶点的出弧链表表头(把所有的出弧连成链表)
Node(int x):val(x),link(nullptr){}
};
Node*A = Node(1);
Node*B = Node(2);
Node*C = Node(3);
Node*D = Node(4);
邻接表的出弧
a、d称为顶点A的出弧,我们用一个链表来储存。
struct Arc
{
int val; //出弧数据
Arc* next; //指向下一条弧的指针
Arc(int x):val(x),next(nullptr){}
};
Arc*a = Arc(5);
Arc*b = Arc(6);
Arc*c = Arc(7);
Arc*d = Arc(8);
a->next = d;
A->head = a; //A顶点的出弧是a、d
B->head = b; //B的出弧是b
D->head = c; //D的出弧是c
这样就把出弧存储在顶点中。
接着创建一个存储顶点的顶点数组。
struct Map
{
vector<Node*>map; //顶点数据,存放顶点
};
3.十字链表
顶点在2的基础上,多了一个入弧(b、c)连接而成的链表。
//A:1 B:2 C:3 D:4
struct Node
{
int val; //顶点数据
Arc* head; //顶点的出弧链表表头(把所有的出弧连成链表)
Arc* tail; //顶点的入弧链表表头
Node(int x):val(x),link(nullptr){}
};
Node*A = Node(1);
Node*B = Node(2);
Node*C = Node(3);
Node*D = Node(4);
4.邻接多重表
这个不是很懂,欢迎大家评论区补充。
三、图的遍历
1.深度优先搜索
2.广度优先搜索
3.最小生成树(把所有点连接起来,并且边的和最小)
n个顶点的连通图的生成树有n-1条边。