参考:
vector的使用 https://blog.csdn.net/tpriwwq/article/details/80609371
图分为有向图 无向图
存图的几种方法
较好的存图能满足的要求
1)能表示图中的结点相连的情况
2)获得能表示图中的结点相连边的权重
3)考虑空间 像邻接矩阵的空间复杂度为O(n^2)
4)能知道一个结点 所连的所有结点的边的情况
1、邻接矩阵
使用二维数组map[][]存储两个边的连接情况
可以map[i][j] = 1表示 i和j是有边的 是连接的
也可以map[i][j] = w(权重) 表示i和j之间边的权重
空间复杂度为O(n^2) 复杂度过高 不满足第3条
2、边的数组 Edge[maxn]
我们可以使用Edge类,它表示两个int实例变量。这种表示方法很简单
但是不能知道一个结点 所连的所有结点的边的情况 不满足第4条
3、邻接表数组(效果最好) 本节未给出该实现代码
我们可以使用一个以顶点为索引的列表数组,其中的每个元素都是和该顶点相邻的顶点列表
都能满足
4、链式前向星(中规中矩)
w是起点序号 cnt表示边的序号 edges[maxn] 表示边的数组 边的序号其实就是edges数组的下标
head[w] 存放的是最新一条边的序号 边里面的属性包含to w和next(起点的上一条边序号)
所以 head[w]放了最新一条边的序号 这条边记录这上一条边序号 这个迭代过程的边都是以w为起点的边
循环起点的所有边时
从i=head[w]最新的边序号开始 遍历edges数组 然后i=边里储存的next(上一条边序号) 直到i = 0了(因为head[w]起初默认为0) 也就是上一条没边了 即刚刚循环到的边就是w连接的第一条边
head[w]起初默认为0
用数组模拟链表
struct Edge{
int to, w, next;
}edges[MAXM];
int head[MAXN], cnt; // cnt为当前边的编号
void add(int from, int to, int w){
edges[++cnt].w = w; //新增一条编号为cnt+1的边,边权为w
edges[cnt].to = to; //该边的终点为to
edges[cnt].next = head[from]; //以form为起点的这条边的next指向上一条from为起点的最新一条的边
head[from] = cnt; //该边成为当前起点最新的第一条边
}
设一个edges数组 用来表示边 用cnt 表示边的符号
head[maxn] 表示当前顶点为起点的最新的一条边
edge 中的to表示终点 w表示权重 next表示以from为起点的上一条最新的一条边
比如
加入顺序为 1-2、1-3、1-4
1、刚开始 head[maxn]初始化为0 边的序号cnt为0
2、那么 加入边1-2 cnt=1 to为2 next为0 head[from] = 1;
//next是起点上一条最新的边 加入边1-2 自然更新1起点最新边为边1-2
3、加入边1-3 cnt=2 to为3 原先head[1]=1 所以next = 1 然后更新head[from] = 2 为该边
4、加入边1-4 cnt=3 to为4 原先head[1]=2 所以next = 2 然后更新head[from] = 3 为该边
遍历的时候
先从起点x最新的边搜起 然后找到 边的next 一直找下去 直到next=0 说明起点x连接的边遍历完成
顺序 是从最晚添加的边慢慢到最早添加的边
//打印2号顶点能到达的所有点
for (int e = head[2]; e != 0; e = edges[e].next)
printf("%d ", edges[e].to);
总结来说 用最新的边来记录起点连接的上一条边 用head来记录起点最新的边 边的next=head[起点]也就是上一条最新的边 然后head[起点] = 最新的边
head[]记录的是边的序号 边的next记录的也是边的序号
//链式前向星边
class lianshiEdge{
public:
int from;
int to; //表示终点
int w; //表示权重
int next; //表示起点的上一条最新边
public:
lianshiEdge(){
}
lianshiEdge(int from, int to, int w){
this->from = from;
this->to = to;
this->w = w;
}
};
//链式前向星
class lianshiqxx{
public:
lianshiEdge edges[maxn]; //边的集合
std::vector<lianshiEdge> adj[maxn]; //起点连接的边的集合
int head[maxn]; //表示起点的最新一条边
int cnt; // cnt为当前边的编号
public:
lianshiqxx(){
this->cnt = 0;
memset(head, 0, sizeof(head));
}
void init(){
this->cnt = 0;
memset(head, 0, sizeof(head));
lianshiEdge e;
for(int i = 0; i < maxn; i++){
edges[i] = e;
}
}
//添加边
void addEdge(lianshiEdge e){
int from = e.from;
int to = e.to;
int w = e.w;
edges[++cnt].w = w; //新增一条编号为cnt+1的边,边权为w
edges[cnt].to = to; //该边的终点为to
edges[cnt].next = head[from]; //以form为起点的这条边的next指向上一条from为起点的最新一条的边
head[from] = cnt; //该边成为当前起点最新的第一条边
}
//获取某个点的连接的边集合 返回集合
std::vector<lianshiEdge> getAdj(int s){
if(adj[s].size()){ //避免重复添加
adj[s].clear();
}
for (int e = head[s]; e != 0; e = edges[e].next){
adj[s].push_back(edges[e]); //添加边
}
return adj[s]; //返回边 集合
}
//获取某个点的连接的边集合 无返回值
void getAdjNotReturn(int s){
if(adj[s].size()){ //避免重复添加
adj[s].clear();
}
for (int e = head[s]; e != 0; e = edges[e].next){
adj[s].push_back(edges[e]);
}
}
//打印
void printAdj(int s){
getAdjNotReturn(s);
for (std::vector<lianshiEdge>::iterator e = adj[s].begin(); e != adj[s].end(); e++)
{
cout<<(*e).from<<(*e).to<<(*e).w<<(*e).next<<endl;
}
}
};
int main(){
//链式前向星测试
lianshiqxx lsqxx;
lianshiEdge e1(1, 2, 1);
lianshiEdge e2(1, 3, 2);
lianshiEdge e3(1, 4, 3);
lsqxx.addEdge(e1);
lsqxx.addEdge(e2);
lsqxx.addEdge(e3);
lsqxx.printAdj(1);
}