链式前向星!!!!!!!!

    我们先要知道这个东西是干什么的,用来模拟链表,替代vector的。原理也是模拟链表的头插。

我们这个前向星长这个样子:
struct node
{
int u;
int v;
int next;
} p[11111];
p[]表示编号,u是边的起始,v是通向何点。如果有权值,你再加个权值就好了。关键是next,你把它想象成一个链表的话,链表里的next是个直接指向与之串联的下一个,这个next想要充当的作用也是如此,这个next的值是与之串联的下个的编号,因为编号是唯一的。
此外我们还要一个数组,表示某个起始点位置,通向的下个位置的编号。
int next【11111】你可以让它全初始为-1也可是0,初始为-1的话你的编号可以从0开始,初始0的话,你得从编号1开始存。至于为什么,先看完全篇再来回答,这是个个人习惯而已。
**重要的是:说过了是模拟头插,而且链表的指针是直接能指向下一个,但是我们的next是存的指向下一个的编号.
举个例子就明白了: 如果1能通向2,3,4。
比如 1->2 1->3 1->4 的编号分别是3 5 9,
编号 u v

3 1 2

5 1 3

9 1 4

我们的next【1】就 = 9;就是存的最后获得数据的编号,为什么?因为是头插!到这还不理解没关系,我们继续说。 就我说的这个数据。next【1】 = 9;那p[9].next存的就是它指向的下一个的编号也就是p[9].next = 5. 有编号了,你就知道里面的p[9].v了啊。再接着编号5里面的p[5].next通向的就是下一个编号了啊!!!也就是p[5].next = 3. 你也就知道p[3].v了。然后发现p[3].next = 0或者等于-1(就是你next初始的值),就表示1能通向的点遍历完了!!!如果这是链表,我们就可以把编号串起来:9->5->3。
之所以说头插,你存的时候顺序是3->5->9;但是遍历是反过来的。
**
是不是超级容易。

这你懂了起始你就知道了遍历的代码了:
你要找u能到那:
for(int i = next[u]; i != 0; i = p[i].next)         
{
               printf("%d\n",p[i].v);
}

还没说创建的代码,上边的理解了,就很好理解:

有个东西我也是第一次用,说一下:
    p[n] = (node){a, b, c};
    这个和数组一样的意思,数组的a[3] = {1, 2, 3} 的意思不说了;
    struct node 
{
     int u;
     int v;
     int next;
}  p[11111];    

    这个也就是p[n].u = a, p[n].v = b, p[n].next = c;   和数组一样,这个结构体存的时候是按照
    你定义的顺序,先定义的u,你存的第一个值就存到u这里面了。
    ans表示的是编号。
void add(int u, int v)
{
    p[++ans] = (node){u, v, next[u]};
    next[u] = ans;
}
上边的遍历你知道了,所以存的时候这个next一直是你存的最后一个的编号。
p[n].next = next [u] 就是指向的上一个编号上边也说了,存的时候和遍历的结果是相反的顺序哦;

看道例题:
图的基本存储的基本方式一
Time Limit: 1800 ms Memory Limit: 65536 KiB
Submit Statistic
Problem Description

解决图论问题,首先就要思考用什么样的方式存储图。但是小鑫却怎么也弄不明白如何存图才能有利于解决问题。你能帮他解决这个问题么?
Input

多组输入,到文件结尾。
每一组第一行有两个数n、m表示n个点,m条有向边。接下来有m行,每行两个数u、v代表u到v有一条有向边。第m+2行有一个数q代表询问次数,接下来q行每行有一个询问,输入两个数为a,b。
注意:点的编号为0~n-1,2<=n<=5000 ,n*(n-1)/2<=m<=n*(n-1),0<=q<=1000000,a!=b,输入保证没有自环和重边
Output

对于每一条询问,输出一行。若a到b可以直接连通输出Yes,否则输出No。
Sample Input

2 1
0 1
2
0 1
1 0
Sample Output

Yes
No

#include <bits/stdc++.h>
#define mm 550000
using namespace std;
int ans;
int Next[mm];                             //是Next不是next是因为不与编译器冲突。
struct node
{
    int u, v, Next;
} p[mm];

void add(int u, int v)
{
    p[++ans] = (node){u, v, Next[u]};
    Next[u] = ans;
}
int main()
{
    int m, n;
    while(cin>>m>>n)
    {
    memset(Next, 0, sizeof(Next));
    memset(p, 0, sizeof(p));
        ans = 0;
        for(int i = 0; i < n; i++)
        {
            int u, v;
            cin>> u >> v;
            add(u, v);
        }
        int t;
        cin >> t;
        while(t--)
        {
            int u, v;
            cin >> u >> v;
            int flag = 0;
            for(int i = Next[u]; i != 0; i = p[i].Next)
            {
                if(p[i].v == v)
                {
                    flag = 1;
                }
            }
            if(flag)
                cout << "Yes" << endl;
            else
                cout << "No" << endl;
        }
    }
    return 0;
}
都完了,是不是忘了我前边说的问题,为什么next数组是初始-1还是0。
这到题路的点是有0的,如果我的编号是从0开始的,存的时候
那么我的p[0].u = 0, p[0].v= 1,p[0].next = 0,next[0] = 0;
遍历时for(int i = Next[u]; i != 0; i = p[i].Next)刚开始i就等于0;根本不行。
如果编号从1开始:
p[1].u = 0, p[1].v= 1,p[1].next = 0,next[0] = 1;
这样就没毛病了。

如果让next初始为-1,或者其他,遍历时只要把i != ?改了就行
for(int i = Next[u]; i != -1; i = p[i].Next)  原理懂了,这个根本不会有任何困扰,
就是个人习惯;
             曾经在一群优秀的人当中,误以为自己也很优秀,而忘了努力。
             这句话每次看都羞愧。加油吧。

猜你喜欢

转载自blog.csdn.net/weixin_43822064/article/details/86252683