图论:链式前向星 邻接表 邻接矩阵 实现+搜索

17 篇文章 0 订阅
本文详细介绍了图的两种常见存储结构——链式前向星和邻接矩阵,并提供了无向图的邻接表实现。通过实例展示了链式前向星如何实现有向图的存储,以及深度优先搜索(DFS)和广度优先搜索(BFS)算法。代码示例清晰地解释了如何添加边、遍历边以及进行搜索操作。
摘要由CSDN通过智能技术生成

目录

有向图

链式前向星(常用 重点)

邻接矩阵

无向图

邻接表


有向图

链式前向星(常用 重点)

链式前向星他讲的很不错

这个是倒序存储的,我们一般是倒序输出的,比如这篇

https://mp.csdn.net/mp_blog/creation/editor/119586896

代码+模拟版

/*链式前向星:静态链表实现。添加节点:为了保证O(1)的添加操作,每条边都加在头结点的后面,这就导致本来在头结点后面的节点的前驱结点变为新添加的节点
而新添加的节点为头结点,也就是说正序输入,倒序添加,可以参考gif,用文字叙述如下
例如 V1 ->2,10 ->3,30(这里权值放在终点上,表示边权)的添加过程(暂且忽略权值,仅显示to,next) (^表示NULL,->表示next的地址)
V1 -> 2 ^   -----    V1->  3 -> ,2 ^		*/
#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>
#define MAX 1000
#define INF 0x3fffffff  //无穷大
using namespace std;
struct node { //静态链表的实现
	int to,next,w;//to:边的终点 next:链表中下一节点地址,实际上是同一起点输入的上一条边的地址,因为倒序存储被反转了 w:当前边的权值
} edge[MAX*MAX];

int n,m;
bool vis[MAX];//vis存储的是边!!每个cnt代表一条边!
bool vi[MAX][MAX];
int head[MAX];//表示以i为起点的第一条边存储的位置,实际上你会发现这里的第一条边存储的位置,其实在以i为起点的所有边的最后输入的那个编号,默认为-1
int cnt = 0;//cnt是下标,在静态链表里的地址,每个cnt代表一条边 
void addedge(int from,int to,int w) { //为了保证O(1)的添加操作,每条边都加在头结点的后面 正序输入,倒序添加 详见第2行注释
	edge[cnt] = {to,head[from],w};//to,next,w next=head[from],也就是说该节点要取代第一条边
	printf("cnt=%d from=%d to=%d,next=%d,w=%d,",cnt,from,to,head[from],w);
	head[from] = cnt++;//取代
	printf("head[%d]=%d\n",from,head[from]);
}
//^ NULL -> -1;	下面为模拟
/*输入
1 2
2 3
3 4
1 3
4 1
1 5
4 5
模拟
edge[0].to = 2;     edge[0].next = -1;      head[1] = 0;
edge[1].to = 3;     edge[1].next = -1;      head[2] = 1;
edge[2].to = 4;     edge[2],next = -1;      head[3] = 2;
edge[3].to = 3;     edge[3].next = 0;       head[1] = 3;
edge[4].to = 1;     edge[4].next = -1;      head[4] = 4;
edge[5].to = 5;     edge[5].next = 3;       head[1] = 5;
edge[6].to = 5;     edge[6].next = 4;       head[4] = 6;
很明显,head[i]保存的是以i为起点的所有边中编号最大的那个,而把这个当作顶点i的第一条起始边的位置.
这样在遍历时是倒着遍历的,也就是说与输入顺序是相反的,不过这样不影响结果的正确性.
比如以上图为例,以节点1为起点的边有3条,它们的编号分别是0,3,5   而head[1] = 5
我们在遍历以u节点为起始位置的所有边的时候是这样的:
for(int i=head[u];~i;i=edge[i].next)  //~1:-1按位取反是0
那么就是说先遍历编号为5的边,也就是head[1],然后就是edge[5].next,也就是编号3的边,然后继续edge[3].next,也
就是编号0的边,可以看出是逆序的.		*/
void dfs(int now)
{
    vis[now] = true;
    cout << now <<"\t";
    for(int i=head[now];~i;i=edge[i].next)//~1:-1按位取反是0
        if(!vis[edge[i].to])
            dfs(edge[i].to);
}
void Dfs(int now) {//深搜回路 如果节点有交叉,请同一个起点的输入顺序变为递增,不同起点允许穿插,比如1 2,2 3,3 1,4 5,3 4,5 3 
	for(int i=head[now]; ~i; i=edge[i].next) { //~1:-1按位取反是0
		int to=edge[i].to;
		if(!vi[now][to]){
			vi[now][to]=1;
			printf("%d %d\n",now,to);
			Dfs(to);
		}
	}
}
queue<int> que;

void bfs(int beg) {
	que.push(beg);
	vis[beg] = true;
	while(!que.empty()) {
		int top = que.front();
		que.pop();
		cout<<top<<"\t";
		for(int i=head[top]; ~i; i=edge[i].next) {
			int to = edge[i].to;
			if(!vis[to]) {
				que.push(to);
				vis[to] = true;
			}
		}
	}
}
int main() {
	int x,y,w;
	cin>>n>>m;
	memset(vis,false,sizeof(vis));
	memset(head,-1,sizeof(head));
	for(int i=0; i<m; i++) {
		cin>>x>>y;
		addedge(x,y,1);
	}
	Dfs(1);
	return 0;
}
/*5 5
0 1 1
0 2 1
0 4 1
1 3 1
2 4 1*/

模板

#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>
#define MAX 1000
#define INF 0x3fffffff  //无穷大
using namespace std;
struct node{//静态链表的实现
    int to,next,w;//to:边的终点 next:链表中下一节点地址,实际上是同一起点输入的上一条边的地址,因为倒序存储被反转了 w:当前边的权值
}edge[MAX*MAX];
int n,m;
bool vis[MAX];
int head[MAX];//表示以i为起点的第一条边存储的位置,实际上你会发现这里的第一条边存储的位置,其实在以i为起点的所有边的最后输入的那个编号,默认为-1
int cnt = 0;//cnt是下标,在静态链表里的地址
void addedge(int from,int to,int w)//为了保证O(1)的添加操作,每条边都加在头结点的后面 正序输入,倒序添加 详见第2行注释
{
    edge[cnt] = {to,head[from],w};//to,next,w next=head[from],也就是说该节点要取代第一条边
    head[from] = cnt++;//取代
}
void dfs(int now)
{
    vis[now] = true;
//具体操作栏  cout << now <<"\t";
    for(int i=head[now];~i;i=edge[i].next)//~1:-1按位取反是0
        if(!vis[edge[i].to])
            dfs(edge[i].to);
}
queue<int> que;
void bfs(int beg)
{
    que.push(beg);
    vis[beg] = true;
    while(!que.empty()){
        int top = que.front();
        que.pop();
//具体操作栏        cout<<top<<"\t";
        for(int i=head[top];~i;i=edge[i].next){
            int to = edge[i].to;
            if(!vis[to]){
                que.push(to);
                vis[to] = true;
            }
        }
    }
}
int main()
{
    return 0;
}

邻接矩阵

#include<cstdio>
#include<iostream>
#include<cstring> 
#include<queue>
#define MAX 1000
#define INF 0x3fffffff  //无穷大
using namespace std;
int n,m;
int g[MAX][MAX];    //邻接矩阵
bool vis[MAX];
void dfs(int now)
{
    vis[now] = true;
//具体操作栏  cout << now <<"\t";
    for(int i=0;i<n;i++){
        if(!vis[i] && g[now][i]!=INF){
            dfs(i);
        }
    }
    return ;
}
queue<int> que;
void bfs(int beg)
{
    que.push(beg);
    vis[beg] = true;
    while(!que.empty()){
        int top = que.front();
        que.pop();
//具体操作栏      cout<<top<<"\t";
        for(int i=0;i<n;i++){
            if(!vis[i] && g[top][i] != INF){
                que.push(i);
                vis[i] = true;
            }
        }
    }
}
int main()
{
    int x,y,w;
    cin>>n>>m;
    for(int i=0;i<n;i++){//不能用memset 
    	for(int j=0;j<n;j++){
    		g[i][j] = INF;
		}
	}
    memset(vis,false,sizeof(vis));
    for(int i=0;i<m;i++){
        cin>>x>>y>>w;
        g[x][y] = w;
        g[y][x] = w;
    }
    //dfs(0);
    bfs(0);
    return 0;
}
/*5 5
0 1 1
0 2 1
0 4 1
1 3 1
2 4 1*/

无向图

邻接表

也可以存储有向图,[x][y]和[y][x]都存储就行,但是一般用链式前向星

#include<cstdio>
#include<iostream>
#include<cstring> 
#include<queue>
#define MAX 1000
#define INF 0x3fffffff  //无穷大
using namespace std;
int n,m;
vector<int> g[MAX];    //邻接表 
bool vis[MAX];
void dfs(int now)
{
    vis[now] = true;
//具体操作栏    cout << now <<"\t";
    int len = g[now].size();
    int to;
    for(int i=0;i<len;i++){
        to = g[now][i];
        if(!vis[to]){
            dfs(to);
        }
    }
}
queue<int> que;
void bfs(int beg)
{
    int len,to;
    que.push(beg);
    vis[beg] = true;
    while(!que.empty()){
        int top = que.front();
        que.pop();
        cout<<top<<"\t";
        len = g[top].size();
        for(int i=0;i<len;i++){
            to = g[top][i];
            if(!vis[to]){
                dfs(to);
            }
        }
    }
}
int main()
{
    int x,y,w;
    cin>>n>>m;
    memset(vis,false,sizeof(vis));
    for(int i=0;i<m;i++){
        cin>>x>>y>>w;
        g[x].push_back(y);
    }
    //dfs(0);
    bfs(0);
    return 0;
}
/*5 5
0 1 1
0 2 1
0 4 1
1 3 1
2 4 1*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值