ps:树是一种特殊的图,树有自己特殊的存储方式,图的存储方式都能应用于树。
对于图、树来讲,一般给出一个n表是有n个节点(标号1~n)m个二元组(a,b)表示ab之间有一条边。这样就能确定一个图。
对于树来讲没有环,所以m=n-1
part one、邻接矩阵
邻接矩阵的优点是可以O(1)查出两点之间有没有边,缺点是无法高效的查找某个点的所有边,且对于稀疏图来讲浪费了大量的空间(存了很多0)。
使用mp[maxn][maxn]二维数组存储,mp[i][j]=0表示节点i,j之间不直接相连,为1表示有一条边相连(当然对于简单图来说只能为1和0)。
int a,b,n,m;
cin>>n>>m;
memset(mp,0,sizeof(mp));//清0
for(int i=0;i<n;i++){
scanf("%d%d",&a,&b);//a和b之间有边
mp[a][b]=mp[b][a]=1;//无向图两个都要赋值
}
这样图就存好并可以使用了。
part two、邻接表
邻接表可以最高效率地查找某个点的所有边,但是无法高效地查询两个点之间是否有边。
使用链表存储(实际应用时为了方便用vector):只记录与点i有边的点,这样相当于记录边。相当于弱化的十字链表(多用了空间)
vector<int>mp[maxn];
cin>>n>>m;
for(int i=1;i<=m;i++){
scanf("%d%d",&a,&b);
mp[a].push_back(b);
mp[b].push_back(a);
}
part three、链式前向星
前向星也是存储边。个人认为前向星只是邻接表的另一种写法,数组模拟链表空间分配(内存池)的一种写法。
好像单纯学前向星没有什么创新的知识,但由于看网上代码尤其是树链剖分和lca很多都用链式前向星的写法,还是老老实实理解一下前向星书写套路。
其实照着邻接表的思路理解链式前向星就很容易了(吐槽一下为什么起一个这么貌似高大上的名字,老老实实叫邻接表不好吗)
const int maxn = 10005;
const int maxm = 1000005;//edge
using namespace std;
int n;
struct node {//存储边的数据结构,to是点,next是指针。
int to, next;
// int value ,from,
};
node edge[maxm];//存储边的数组,兼任内存池。
int box[maxn];//记录节点i邻接表第一个成员指针,使用头插法。
int ecnt;//边的个数
inline void _make_map(int from, int to) { //建立从from到to的一条单向边,若无向图则正反建立两条。 头插法
edge[ecnt].to = to;//to 节点
edge[ecnt].next = box[from];//同节点下该边下一条边
box[from] = ecnt++;// 节点from的第一条边
}
inline void make_map(int from, int to)//双向边
{
_make_map(from, to);//正反向两次双向边
_make_map(to, from);
}
int main() {
while (scanf("%d", &n) != EOF) {
ecnt = 0;
int i;
int u[100], v[100];
// 储存边
for (i = 0; i < n; i++) {
scanf("%d%d", &u[i], &v[i]);
make_map(u[i], v[i]);
}
}
return 0;
}