链式向前星,是图的一种存储结构,采用数组模拟链表实现前向星的功能,简单来讲,它按照边来存图,而邻接矩阵是按照点来存图。
可以结合图的邻接表存储(链表实现)图的存储和数组实现链表数组实现链表的知识,自己实现链式向前星。
链式向前星
无边权图
存储结构
const int N = 1000, M = N * 2;
int h[N], e[M], ne[M], idx;
h[i]
表示第i
个结点的头节点
e[i]
表示第i
个节点在图中对应的编号
ne[i]
表示第i
个节点的下一个节点所在的下标,也就是边
idx
表示下一个新添节点的下标
注意头节点需要N
个的话,那么,最多需要 N*(N-1)
个ne节点和e节点(完全图的性质)。但是一般都没有那么满,满图用邻接矩阵来存,所以一般M = N * 2
就够了。
初始化
void init() {
idx = 0;
memset(h, -1, sizeof(h));
}
在一般适合,直接全局写就行了,不用写成函数再调用。
加边操作
void add(int a, int b) { // 添加边 : a -> b
// 就是对 h[a] 的链表添加一个节点,新节点值为 b
e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}
链表头插法添加节点:
void add(int x) { e[idx] = x; ne[idx] = head; head = idx++; }
注意调用一次是有向图,可以调用两次变成双向图或无向图
即:
add(a, b);
add(b, a);
遍历操作
普通遍历
// 遍历
void disp() {
for (int i = 0; i < N; ++i) {
if (h[i] != -1){
cout << i << ":";
for(int j = h[i]; j != -1; j = ne[j]) //遍历顶点指向的边
cout << e[j] << " idx(" << j << ") -> ";
cout << endl;
}
}
}
dfs遍历
见我主页其他文章
bfs遍历
见我主页其他文章
完整代码
#include <iostream>
using namespace std;
const int N = 1000, M = N * 2;
int h[N], e[M], ne[M], idx;
void init() {
idx = 0;
memset(h, -1, sizeof(h));
}
void add(int a, int b) { // 添加边 : a -> b
// 就是对 h[a] 的链表添加一个节点,新节点值为 b
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
// 遍历
void disp() {
for (int i = 0; i < N; ++i) {
if (h[i] != -1){
cout << i << ":";
for(int j = h[i]; j != -1; j = ne[j]) //遍历顶点指向的边
cout << e[j] << " idx(" << j << ") -> ";
cout << endl;
}
}
}
int main () {
init();
add(1,2);
add(3,4);
add(2,3);
add(2,1);
add(4,5);
add(5,1);
add(5,2);
disp();
}
有边权图
存储结构
const int N = 1000, M = N * 2;
int h[N], e[M], w[M], ne[M], idx;
h[i]
表示第i
个结点的头节点
e[i]
表示第i
个节点在图中对应的编号
w[i]
表示第i
条边的边权
ne[i]
表示第i
个节点的下一个节点所在的下标,也就是边
idx
表示下一个新添节点的下标
初始化
void init() {
memset(h, -1, sizeof(h));
idx = 0;
}
加边操作
// 添加边, a->b , 边权为 c
void add(int a, int b, int c) {
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}
遍历操作
有边权图的遍历其实就只是比无边权的多输出
w[j]
的值而已。
普通遍历
// 普通遍历
void disp() {
for (int i = 0; i < N; ++i) {
if (h[i] != -1) {
cout << i << ": ";
for (int j = h[i]; j != -1; j = ne[j]) {
cout << e[j] << " " << w[j] << "(" << j << ") -> ";
}
cout << endl;
}
}
}
dfs遍历
bool st[N];
// dfs遍历
void dfs(int i) {
st[i] = true;
cout << i;
for (int j = h[i]; j != -1; j = ne[j]) {
if (!st[e[j]]) {
cout << " -(" << i << ")" << w[j] << "> ";
dfs(e[j]);
}
}
}
bfs遍历
bfs遍历带权图很麻烦,带权图还是用dfs遍历算了。
void bfs(int i) {
qu[++rear] = i;
st[i] = true;
n[i] = 0;
int father = i;
while (front != rear) {
int t = qu[++front];
if (n[father] == 0) {
father = t;
}
for (int j = h[t]; j != -1; j = ne[j]) {
if (!st[e[j]]) {
qu[++rear] = e[j];
quw[++rearw] = w[j];
st[e[j]] = true;
n[t]++;
}
}
if (frontw == rearw) {
cout << t;
break;
}
cout << t << " -(" << father << ")" << quw[++frontw] << "> ";
n[father]--;
}
}