C++之链式前向星

基本定义以及实现

(若果有一定了解,可以跳过第一部分,直接看第二部分)
我们首先来看一下什么是前向星.
前向星是一种特殊的边集数组,我们把边集数组中的每一条边按照起点从小到大排序,如果起点相同就按照终点从小到大排序,
并记录下以某个点为起点的所有边在数组中的起始位置和存储长度,那么前向星就构造好了.

用**len[i]来记录所有以i为起点的边在数组中的存储长度.
head[i]**记录以i为边集在数组中的第一个存储位置.

那么对于下图:
在这里插入图片描述
我们输入边的顺序为:

1 2

2 3

3 4

1 3

4 1

1 5

4 5

那么排完序后就得到:

编号: 1 2 3 4 5 6 7

起点u: 1 1 1 2 3 4 4

终点v: 2 3 5 3 4 1 5

得到:

head[1] = 1 len[1] = 3
head[2] = 4 len[2] = 1
head[3] = 5 len[3] = 1
head[4] = 6 len[4] = 2

但是利用前向星会有排序操作,如果用快排时间至少为O(nlog(n))

如果用**链式前向星,**就可以避免排序.

我们建立边结构体为:

struct Edge
{
     int next;
     int to;
     int w;
};
 

其中edge[i].to表示第i条边的终点,edge[i].next表示与第i条边同起点的下一条边的存储位置,edge[i].w为边权值.

另外还有一个数组head[],它是用来表示以i为起点的第一条边存储的位置,实际上你会发现这里的第一条边存储的位置其实

在以i为起点的所有边的最后输入的那个编号.

head[]数组一般初始化为-1,对于加边的add函数是这样的:

void add(int u,int v,int w)
{
    edge[cnt].w = w;
    edge[cnt].to = v;
    edge[cnt].next = head[u];
    head[u] = cnt++;
}

初始化cnt = 0,这样,现在我们还是按照上面的图和输入来模拟一下:

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)

那么就是说先遍历编号为5的边,也就是head[1],然后就是edge[5].next,也就是编号3的边,然后继续edge[3].next,也

就是编号0的边,可以看出是逆序的.

模板代码1:

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 #include <queue>
 5 using namespace std;
 6 const int inf = 0x3f3f3f3f;
 7 const int M = 4444;
 8 int d[M],head[M],vis[M];
 9 struct nod{
10     int nex,to,w;
11 }eg[M];
12 typedef pair<int,int> P;
13 int cnt=0;
14 inline void add(int u,int v,int w){
15     eg[cnt].to=v;
16     eg[cnt].w=w;
17     eg[cnt].nex=head[u];
18     head[u]=cnt++;
19 }
20 void dijkstra(int s){
21     priority_queue<P,vector<P>,greater<P> >que;
22     
23     d[s]=0;
24     que.push(P(0,s));
25     while(!que.empty()){
26         P p = que.top();
27         que.pop();
28         int v=p.second;
29         if(d[v]<p.first) continue;
30         for(int i=head[v];~i;i=eg[i].nex){
31             nod e=eg[i];
32             if(e.w+d[v]<d[e.to]){
33                 d[e.to]=e.w+d[v];
34                 que.push(P(d[e.to],e.to));
35             }
36         }
37     }
38 }
39 int main(){
40     int t,n;
41     scanf("%d %d",&t,&n);
42     memset(d,inf,sizeof(d));
43     memset(head,-1,sizeof(head));
44     for(int i=0;i<t;i++){
45         int u,v,cost;
46         scanf("%d %d %d",&u,&v,&cost);
47         add(u,v,cost);
48         add(v,u,cost);
49     }
50     dijkstra(1);
51     printf("%d\n",d[n]);
52     return 0;
53 }

模板代码2(优化):

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cmath>
 4 #include<string>
 5 #include<vector>
 6 #include<algorithm>
 7 #include<cstdio>
 8 #include<queue>
 9 using namespace std;
10 const int MAX_V = 200010;
11 const int MAX_E = 2000010;
12 const int INF = 0x3f3f3f3f;
13 int V,E,cnt;
14 int heap[MAX_V],dis[MAX_V];
15 
16 struct Edge{
17     int to,next,cost;
18 }rng[MAX_E];
19 void add(int u,int v,int cost){
20     rng[cnt].to = v;
21     rng[cnt].next = heap[u];
22     rng[cnt].cost = cost;
23     heap[u] = cnt++;
24 }
25 struct Rule{
26     bool operator()(int &a,int &b)const{
27         return dis[a] > dis[b];
28     }
29 };
30 inline int read()
31 {
32     int X=0,w=1; char ch=0;
33     while(ch<'0' || ch>'9') {if(ch=='-') w=-1;ch=getchar();}
34     while(ch>='0' && ch<='9') X=(X<<3)+(X<<1)+ch-'0',ch=getchar();
35     return X*w;
36 }
37 void Dijkstra(int a_){
38     memset(dis,INF,sizeof(dis));
39     priority_queue<int,vector<int>,Rule > q;
40     dis[a_] = 0;q.push(a_);
41     
42     while(!q.empty()){
43         int u = q.top();q.pop();
44         for(int k=heap[u];k != -1;k = rng[k].next){
45             int &v = rng[k].to;
46             if(dis[v] > dis[u] + rng[k].cost){
47                 dis[v] = dis[u] + rng[k].cost;
48                 q.push(v);
49             }
50         }
51     }
52 }
53 int main(void){
54     cnt = 0;
55     memset(heap,-1,sizeof(heap));
56     V = read(),E = read();
57     int x,y,z;
58     for(int i=1;i<=E;i++){
59         x = read(),y = read(),z = read();
60         add(x,y,z);
61     }
62     Dijkstra(1);
63     if(dis[V] == INF){
64         printf("-1\n");
65     }
66     else
67         printf("%d\n",dis[V]);
68     return 0;
69 }

链式前向星实现

DFS

图的深度优先遍历,基本思想是访问顶点,然后访问v0邻接到的未被访问的点顶点v1,在从v1出发递归地按照深度优先的方式遍历。当遇到一个所有邻接于它的顶点都被访问了的顶点u时,则回到已访问顶点的序列中最后一个拥有未被访问的相邻顶点的顶点w,从w出发继续访问。最终当任何已被访问的顶点都没有未被访问的相邻顶点时,遍历结束。也就是说深度优先遍历是沿着图的某一条分支遍历,直到末端,然后回溯,沿着另一条分支进行同样的遍历,直到所有的分支都被遍历过为止。

基于链式前向星的代码:

bool s[maxn]={0};//初始化
void dfs(int x)
{
    s[x]=true;//表示被访问过
    printf("%d\n",x);
    int i;
    for(i=head[x];x!=-1;i=edge[i].next)
    {
        if(s[edge[i].to])
           dfs(edge[i].to);
    }
}
#include <stdio.h>
#include <memory.h>
#include <math.h>
#include <cstring>
#include <vector>
#include <set>
#include <stack>
#include <queue>
#include <algorithm>
#include <map>

#define I scanf
#define OL puts
#define O printf
#define F(a,b,c) for(a=b;a<c;a++)
#define FF(a,b) for(a=0;a<b;a++)
#define FG(a,b) for(a=b-1;a>=0;a--)
#define LEN 1000000
#define MAX 1<<30
#define V vector<int>

using namespace std;

int head[LEN];    //记录源点u在mp中第一个地址i=head[u] 调用完之后就可以用mp[i]访问边表mp 
int cnt=0;        //边表下标,随着数据的录入而扩张 
struct edge{    //边 
    int to,next,w;
};
edge mp[LEN];    //边表 

void add(int u,int v,int w){    //增加边 
    mp[cnt].to=v;
    mp[cnt].w=w;
    mp[cnt].next=head[u];    //指向源点u所构成的静态链表的头结点。如果是首次构造链,head[u]=-1 ,相当于NULL 
    head[u]=cnt++;            //更新当前地址 
}

int vis[LEN];
int bk;

void dfs(int s){
    vis[s]=1;
    int i;
    for(i=head[s];~i;i=mp[i].next){
        int to=mp[i].to;
        if(!vis[to] && to!=bk)
            dfs(to);
    }
}

int main(){
//    freopen("battle over city.txt","r",stdin);
    fill(head,head+LEN,-1);
    int n,m,k,a,b,i;
    I("%d%d%d",&n,&m,&k);
    while(m--){
        I("%d%d",&a,&b);
        add(a,b,1);
        add(b,a,1);
    }
    while(k--){
        I("%d",&bk);
        memset(vis,0,sizeof vis);
        int ans=0;
        F(i,1,n+1) if(i!=bk && !vis[i]){
            dfs(i);
            ans++;
        }
        O("%d\n",ans-1);
    }
    return 0;
    
}

BFS

图的宽度优先遍历与深度优先遍历不同之处在于:宽度优先遍历访问顶点v0然后访问v0邻接的未被访问的所有顶点,v1,v2,····vn,在依次访问v1,v2,···vn连接到的未被访问的所有顶点,如此下去,直到所有点都被访问。
代码:

 bool s[maxn];
    void bfs(int x)
    {
        int queue[maxn];
        int iq=0;
        queue[iq++]=x;
        int i=k;
        for(int i=0;i<iq;iq++)
        {
            //每次从队头取节点,一定是下一个待扩展节点;
            printf("%d\n",queue[i]);
            //遍历与当前节点相连接的节点,如果没有未被访问过,入队
            for(k=head[queue[i]];k!=-1;k=edge[k].next)
            {
                if(!s[edge[k].to)
                {
                    queue[iq++]=edge[k].to;
                }
            }
        }
    }
#include <stdio.h>
#include <memory.h>
#include <math.h>
#include <cstring>
#include <vector>
#include <set>
#include <stack>
#include <queue>
#include <algorithm>
#include <map>

#define I scanf
#define OL puts
#define O printf
#define F(a,b,c) for(a=b;a<c;a++)
#define FF(a,b) for(a=0;a<b;a++)
#define FG(a,b) for(a=b-1;a>=0;a--)
#define LEN 100000
#define MAX 1<<30
#define V vector<int>

using namespace std;

int head[LEN];
int vis[LEN];
int cnt=0;
struct edge{
    int to,w,next;
};
edge mp[LEN];

void add(int u,int v,int w){
    mp[cnt].to=v;
    mp[cnt].w=w;
    mp[cnt].next=head[u];
    head[u]=cnt++;
}

int main(){
//    freopen("Forwards on Weibo.txt","r",stdin);
    int N,L,M,K,i,j;
    fill(head,head+LEN,-1);
    I("%d%d",&N,&L);
    F(i,1,N+1){
        I("%d",&M);
        while(M--){
            I("%d",&j);
            add(j,i,1);
        }
    }
    I("%d",&K);
    while(K--){
        I("%d",&i);
        memset(vis,0,sizeof vis);
        queue<int> q;
        q.push(i);
        int lev=0,cnt=0;
        while(!q.empty()){
            int sz=q.size();
            while(sz--){
                int u=q.front();
                q.pop();
                vis[u]=1;
                for(j=head[u];~j;j=mp[j].next){
                    int to=mp[j].to;
                    if(!vis[to]){
                        vis[to]=1;
                        q.push(to);
                        cnt++;
                    }
                }
            }
            lev++;
            if(lev>=L) break;
        }
        O("%d\n",cnt);
    }
    return 0;
    
}

转自:http://blog.csdn.net/acdreamers/article/details/16902023

http://blog.csdn.net/henuwhr/article/details/76668590

前向星和链式前向星的区别在于实现方式和数据结构的不同。 前向星是一种存储图的边的数据结构,它使用两个数组来存储边的信息。一个数组存储每个顶点的第一条边的索引,另一个数组存储每条边的下一条边的索引。这种方式可以方便地遍历每个顶点的所有边。 链式前向星是一种基于链表的存储方式,它使用链表来存储每个顶点的边。每个顶点都有一个指向第一条边的指针,每条边都有一个指向下一条边的指针。这种方式可以动态地添加和删除边。 总结来说,前向星使用数组存储边的信息,链式前向星使用链表存储边的信息。链式前向星相比前向星更加灵活,可以方便地进行边的插入和删除操作。但是链式前向星的空间复杂度较高,因为需要额外的指针来存储链表的连接关系。 引用\[1\]中提到,链式前向星的整体结构很像邻接表,但是实现方式不同。链式前向星的思想和邻接表一致,只是在实现上有所区别。因此,链式前向星的使用和邻接表相一致,可以用于存储和遍历图的边的信息。 #### 引用[.reference_title] - *1* *2* *3* [链式前向星](https://blog.csdn.net/MuShan_bit/article/details/123882339)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值