1.图的基础
图是由节点和边组成的
图的主要应用:交通运输、社交网络、互联网、工作安排、脑区活动、程序状态执行
图的分类:有向图和无向图;有权图和无权图;连通图和非连通图
图的连通性:如果从图的任意一个结点可以到另外任意一个结点,说明这个图是连通的,下面这个图不是完全连通的
简单图:没有自环边和平行边的(一般题目处理的都是简单图,如果没有说明是简单图,需要处理一下)
自环边:有自己到自己的一条路径
平行边:代表两个节点之前有多条连接路径。比如交通运输:从A点到B点有多条路径
2.图的表示
图有两种表示方式:邻接矩阵、邻接表
邻接表适合表示稀疏图(边的个数远远小于这个图最大可以连接的数量)
邻接矩阵适合表示稠密图。
选择表达方式的时候主要考虑空间复杂度,
邻接矩阵的空间复杂度为O(V^2)
邻接表的空间复杂度为O(E)
一般都用邻接表。稠密的时候可以考虑用邻接矩阵。V是结点个数,E是边的条数
邻接矩阵的代码:
无权邻接矩阵和有权邻接矩阵的区别在于。无权邻接矩阵用0,1表示边
有权邻接矩阵,用权值w表示边(如果边的权值可以为0的话,那么就需要将边写成一个结构体,参考邻接表的写法)
#include <iostream>
#include <vector>
using namespace std;
//使用邻接矩阵来表示图
class DenseGraph{
private:
int n;//n代表结点数
vector<vector<int> > Graph;//邻接矩阵
bool directed;//是否是有向图
public:
DenseGraph(int n,bool directed){
this->n = n;
this->directed = directed;
for(int i=0; i<n; i++)
Graph.push_back(vector<int>(n,0));
}
//无权图:给图添加一条边
void addEdge(int v,int m)
{
Graph[v][m] = 1;
if(!directed) //无向图
Graph[m][v] = 1;
}
//有权图:给图添加一条边
void addEdge(int v,int m,int w)
{
Graph[v][m] = w;
if(!directed) //无向图
Graph[m][v] = w;
}
void print()
{
for(int i=0; i<n; i++)
{
for(int j=0; j<n; j++)
cout<<Graph[i][j]<<" " ;
cout<<endl;
}
}
};
int main()
{
//无权无向图
DenseGraph g1(7,false);
g1.addEdge(0,1);
g1.addEdge(0,2);
g1.addEdge(0,5);
g1.addEdge(0,6);
g1.addEdge(5,3);
g1.addEdge(3,4);
g1.addEdge(5,4);
g1.addEdge(4,6);
g1.print();
cout<<endl;
//有权有向图
DenseGraph g2(5,true);
g2.addEdge(0,1,5);
g2.addEdge(0,2,2);
g2.addEdge(0,3,6);
g2.addEdge(2,1,1);
g2.addEdge(2,4,5);
g2.addEdge(2,3,3);
g2.addEdge(1,4,1);
g2.addEdge(3,4,2);
g2.print();
return 0;
}
无权邻接表的代码:
#include <iostream>
#include <vector>
using namespace std;
//使用邻接表来表示无权图
class SparseGraph{
private:
int n;//n代表结点数
vector<vector<int> > Graph;//邻接表
bool directed;//是否是有向图
public:
SparseGraph(int n,bool directed){
this->n = n;
this->directed = directed;
for(int i=0; i<n; i++)
Graph.push_back(vector<int>());
}
//给图添加一条边
void addEdge(int v,int w)
{
Graph[v].push_back(w);
if(!directed) //无向图
Graph[w].push_back(v);
}
//判断两个点是否有边 O(n)
bool hasEdge(int w, int v)
{
//查看是否存在w到v的边
//即看 Graph[w]中是否存在一个值的v
for(int i=0; i<Graph[w].size(); i++)
{
if(Graph[w][i]==v)
return true;
}
return false;
}
void print()
{
for(int i=0; i<n; i++)
{
cout<<i<<":";
for(int j=0; j<Graph[i].size(); j++)
cout<<Graph[i][j]<<" " ;
cout<<endl;
}
}
};
int main()
{
//无权有向图
SparseGraph g1(7,true);
g1.addEdge(0,1);
g1.addEdge(0,2);
g1.addEdge(0,5);
g1.addEdge(0,6);
g1.addEdge(5,3);
g1.addEdge(3,4);
g1.addEdge(5,4);
g1.addEdge(4,6);
g1.print();
cout<<g1.hasEdge(0,5)<<endl;
cout<<g1.hasEdge(4,5)<<endl;
return 0;
}
有权邻接表的代码:要使用一个结构体表示结点
#include <iostream>
#include <vector>
using namespace std;
//每个结点用一个结构体来表示
struct node{//新增结构体,表示与某个节点相连的节点号与权值
int num;
int weight;
};
//使用邻接表来表示有权图
class SparseGraph{
private:
int n;//n代表结点数
vector<vector<node> > Graph;//邻接表
bool directed;//是否是有向图
public:
SparseGraph(int n,bool directed){
this->n = n;
this->directed = directed;
for(int i=0; i<n; i++)
Graph.push_back(vector<node>());
}
//给图添加一条边
void addEdge(int v,int w,int weight)
{
node node;
node.num=w;
node.weight = weight;
Graph[v].push_back(node);
if(!directed) //无向图
{
node.num=v;
Graph[w].push_back(node);
}
}
//判断两个点是否有边 O(n)
bool hasEdge(int w, int v)
{
//查看是否存在w到v的边
//即看 Graph[w]中是否存在一个值的v
for(int i=0; i<Graph[w].size(); i++)
{
if(Graph[w][i].num==v)
return true;
}
return false;
}
void print()
{
for(int i=0; i<n; i++)
{
cout<<i<<":";
for(int j=0; j<Graph[i].size(); j++)
cout<<Graph[i][j].num<<"("<<Graph[i][j].weight<<")"<<" " ;
cout<<endl;
}
}
};
int main()
{
//有权有向图
SparseGraph g2(5,true);
g2.addEdge(0,1,5);
g2.addEdge(0,2,2);
g2.addEdge(0,3,6);
g2.addEdge(2,1,1);
g2.addEdge(2,4,5);
g2.addEdge(2,3,3);
g2.addEdge(1,4,1);
g2.addEdge(3,4,2);
g2.print();
cout<<g2.hasEdge(0,3)<<endl;
cout<<g2.hasEdge(4,5)<<endl;
return 0;
}