zoukankan      html  css  js  c++  java
  • 图论算法 数据结构模板

    栈stack:

     1 struct sta
     2 {
     3     int sz[100001];
     4     int top()
     5     {
     6         return sz[top];
     7     }
     8     void push(int x){
     9          sz[++top]=x;
    10     }
    11     void pop(){
    12         if(top>0)
    13         top--;
    14     }
    15     void cl()
    16     {
    17         top=0;
    18     }
    19     int size(){
    20         return top;
    21     }
    22 }stack;
    stack

     队列queue:

     1 #define MAXN 10000
     2 struct queue{
     3     int sz[10000];
     4     int head,tail;
     5     queue()
     6     {
     7         head=0;
     8         tail=0;
     9     }
    10     queue()
    11     {
    12         head=0;
    13         tail=0;
    14         delete []sz;
    15     }
    16     int front()
    17     {
    18         if(!empty())
    19         return sz[head];
    20     }
    21     bool empty()
    22     {
    23         return (head>=0&&tail>=0&&head==tail||head>tail);
    24     }
    25     bool full()
    26     {
    27         return tail>=MAXN;
    28     }
    29     int push(int x)
    30     {
    31         if(!full())
    32         sz[tail++]=x;
    33     }
    34     void pop()
    35     {
    36         if(!empty())
    37             ++head;
    38     }
    39 }
    queue

     Tarjan:

    链接:tarjan算法详解

    代码:

     1 #include<iostream>
     2 using namespace std;
     3 #include"cstdio"
     4 #include<cstring>
     5 int head[1001],DFN[1001],LOW[1001];
     6 struct node{
     7     int next;
     8     int v;
     9 }edge[1001];
    10 int stack[1001],n,m,tot,num,index;
    11 int visit[10001];
    12 void add(int x,int y)
    13 {
    14     edge[++num].next=head[x];
    15     edge[num].v=y;
    16     head[x]=num;
    17     return;
    18 }
    19 void tarjan(int x)//代表第几个点在处理。递归的是点。
    20 {
    21     DFN[x]=LOW[x]=++tot;//新进的点初始化 
    22     stack[++index]=x;//jin jnzhan
    23     visit[x]=1;//表示在栈里
    24     for(int i=head[x];i!=-1;i=edge[i].next)
    25     {
    26         if(!DFN[edge[i].v])
    27         {//如果没访问过
    28             tarjan(edge[i].v);//往下进行延伸,开始递归
    29             LOW[x]=min(LOW[x],LOW[edge[i].v]);//递归出来,比较谁是谁的儿子/父亲,就是树的对应关系,涉及到强连通分量子树最小根的事情。
    30         }
    31         else if(visit[edge[i].v])
    32         {//如果访问过,并且还在栈里。
    33             LOW[x]=min(LOW[x],DFN[edge[i].v]);//比较谁是谁的儿子/父亲。就是链接对应关系
    34         }
    35     }
    36     if(LOW[x]==DFN[x]) //发现是整个强连通分量子树里的最小根。
    37     {
    38         do
    39         {
    40             printf("%d ",stack[index]);
    41             visit[stack[index]]=0;
    42             index--;
    43         }while(x!=stack[index+1]);//出栈,并且输出.
    44         printf("
    ");
    45     }
    46     return ;
    47 }
    48 
    49 void chushi()
    50 {
    51     for(int i=1;i<=n;i++)
    52     head[i]=-1;
    53 }
    54 int main()
    55 {
    56     scanf("%d%d",&n,&m);
    57     chushi();
    58     //memset(head,-1,sizeof(head));
    59     for(int i=1;i<=m;i++)
    60     {
    61         int x,y;
    62         scanf("%d%d",&x,&y);
    63         add(x,y);
    64     }
    65     for(int i=1;i<=n;i++)
    66     if(!DFN[i]) tarjan(i);//当这个点没有访问过,就从此点开始。防止图没走完
    67      return 0;    
    68 }
    tarjan

     SPFA:

    主要思想是:

        初始时将起点加入队列。每次从队列中取出一个元素,并对所有与它相邻的点进行修改,若某个相邻的点修改成功,则将其入队。直到队列为空时算法结束。
        这个算法,简单的说就是队列优化的bellman-ford,利用了每个点不会更新次数太多的特点发明的此算法。
    SPFA 在形式上和广度优先搜索非常类似,不同的是广度优先搜索中一个点出了队列就不可能重新进入队列,但是SPFA中一个点可能在出队列之后再次被放入队列,也就是说一个点修改过其它的点之后,过了一段时间可能会获得更短的路径,于是再次用来修改其它的点,这样反复进行下去。
    算法时间复杂度:O(kE)E是边数。K是常数,平均值为2
    算法实现:
        dis[i]记录从起点si的最短路径,w[i][j]记录连接ij的边的长度。pre[v]记录前趋。
        team[1..n]为队列,头指针head,尾指针tail
        布尔数组exist[1..n]记录一个点是否现在存在在队列中。
        初始化:d[s]=0,d[v]=∞(vs),memset(exist,false,sizeof(exist));
        起点入队team[1]=s; head=0; tail=1;exist[s]=true;
        do
        {1、头指针向下移一位,取出指向的点u
        2、exist[u]=false;已被取出了队列
        3、foru相连的所有点v  //注意不要去枚举所有点,用数组模拟邻接表存储
           if (d[v]>d[u]+w[u][v])
             {   d[v]=d[u]+w[u][v];
                 pre[v]=u;
                 if (!exist[v]) //队列中不存在v点,v入队。
                   {         //尾指针下移一位,v入队;
                        exist[v]=true;
                     }
              }
        }
        while (head < tail);
    循环队列:
      采用循环队列能够降低队列大小,队列长度只需开到2*n+5即可。例题中的参考程序使用了循环队列。
     1 //spfa
     2 
     3 #include<iostream>
     4 #include<cstdio> 
     5 #include<cstring>
     6 using namespace std;
     7 const int maxn=0x7f;
     8 bool vis[1001];
     9 int map[1001][1001],dis[1001],queue[1001],path[1001];
    10 int n,m,head=0,tail=1,now;
    11 
    12 
    13 void spfa(int x)
    14 {
    15     queue[head]=x;
    16     vis[x]=true;
    17     dis[x]=0;
    18     path[x]=x;
    19     while(head<tail)
    20     {
    21         now=queue[head];
    22         for(int i=1;i<=n;i++)
    23         {
    24             if(dis[i]>dis[now]+map[now][i])
    25             {
    26                 dis[i]=dis[now]+map[now][i];
    27                 path[i]=now;
    28                 if(vis[i]==false)
    29                 {
    30                     queue[tail++]=i;
    31                     vis[i]=true;
    32                 }
    33             }
    34         }
    35     vis[now]=false;
    36     head++;
    37     }
    38 }
    39 void print(int st,int en)
    40 {
    41     int q[1001];
    42     int tot=1;
    43     q[tot]=en;
    44     tot++;
    45     int temp=path[en];
    46     while(temp!=st)
    47     {
    48         q[tot]=temp;
    49         tot++;
    50         temp=path[temp];
    51     }
    52     q[tot]=st;
    53     for(int i=tot;i>=1;i--)
    54     {
    55         if(i!=1)
    56           printf("%d -- >",q[i]);
    57         else
    58           printf("%d",q[i]);
    59     }
    60     cout<<endl;
    61 }
    62 int main()
    63 {
    64     memset(map,maxn,sizeof(map));
    65     scanf("%d%d",&n,&m);
    66     int he,ta,len;
    67     for(int i=1;i<=m;i++)
    68     {
    69         cin>>he>>ta>>len;
    70         map[he][ta]=map[ta][he]=len;
    71     }
    72     memset(dis,maxn,sizeof(dis));
    73     memset(vis,false,sizeof(vis));
    74     memset(queue,0,sizeof(queue));
    75     int start,end;
    76     scanf("%d%d",&start,&end);
    77     spfa(start);
    78     printf("%d
    ",dis[end]);
    79     print(start,end);
    80     return 0;
    81 }
    SPFA
     
     
    数组模拟邻接表储存:
    详见博客:数组模拟
    基本思想:对图的每个顶点建立一个单链表,存储该顶点所有邻接顶点及其相关信息。
    每一个单链表设一个表头结点。
    i个单链表表示依附于顶点Vi的边(对有向图是以顶点Vi为头或尾的弧)
     1 #include<iostream>
     2 #include<cstdio>
     3 using namespace std;
     4 const int E=5;//E为最大边数
     5 const int N=3;//n为最大定点数
     6 struct Edge {
     7     int u,v,w,next;//两顶点,权值,下一条边;
     8 } edge[E];
     9 int head[N];//表头;
    10 int num;//内存指针;
    11 void chushi()
    12 {
    13     for(int i=1; i<=N; i++)
    14     head[i]=-1;
    15     num=0;
    16 }
    17 void add_edge(int b,int e,int w)
    18 {
    19     edge[num].u=b;
    20     edge[num].v=e;
    21     //edge[num].w=w;
    22     edge[num].next=head[b];
    23     head[b]=num++;
    24 }
    25 int main() 
    26 {
    27     int n,m,a,b,x;
    28     chushi();
    29     scanf("%d%d",&n,&m);
    30     for(int i=1;i<=m;i++)
    31     {
    32           scanf("%d %d %d",&a,&b/*,&x*/);   //a、b之间有一条长度为x的边 
    33           add_edge(a,b,x);
    34     }
    35     cout<<1;
    36     for(int i=head[1];i!=-1;i=edge[i].next)   //遍历从点1开始的所有边 
    37     {
    38            cout<<"-->"<<edge[i].v;
    39     }
    40     return 0;
    41 }
    邻接表
     
     
     
    广搜 bfs
     1 //bfs
     2 
     3 #include<iostream>
     4 #include<cstdio> 
     5 using namespace std;
     6 int queue[1001],top=0,end=1;
     7 int map[1001][1001];
     8 int vis[1001];
     9 int n,m;
    10 void bfs(int p)
    11 {
    12     queue[end]=p;
    13     vis[p]=1;
    14     printf("%c -->",queue[end]+64);
    15     while(top!=end)
    16     {   
    17         top++;
    18         int k=queue[top];
    19         for(int i=1;i<=n;i++)
    20         {
    21             
    22             if(vis[i]==0&&map[k][i]==1)
    23             {
    24                 printf("%c-->",i+64);
    25                 queue[++end]=i;
    26                 vis[i]=1;
    27             }
    28         }
    29     }
    30 }
    31 int main()
    32 {
    33     char head,tail;
    34     scanf("%d%d",&n,&m);
    35     for(int i=1;i<=m;i++)
    36     {
    37         cin>>head>>tail;
    38         head=head-64;
    39         tail=tail-64;
    40         map[head][tail]=map[tail][head]=1;
    41     }
    42     bfs(1);
    43     return 0;
    44 }
    bfs

    深搜 dfs

     1 #include<iostream>
     2 #include<cstdio>
     3 using namespace std;
     4 char maps[10][11];
     5 bool vis[1000];
     6 int q,m;
     7 void dfs(int a){
     8     vis[a]=1;
     9     if(a<q)
    10     printf("%c --> ",a+64);
    11      else printf("%c",a+64);
    12     for(int i=1;i<=q;i++)
    13     {
    14         if(maps[a][i]==1&&vis[i]==0)
    15         dfs(i);
    16     }
    17 }
    18 int main()
    19 {
    20     
    21     char a,b;
    22     cin>>q>>m;
    23     for(int i=1;i<=m;i++)
    24     {
    25         cin>>a>>b;
    26         maps[a-64][b-64]=1;
    27         maps[b-64][a-64]=1;
    28     }
    29     dfs(1);
    30 }
    dfs

     

    Floyed:

    思路:三层循环松弛中间节点

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 using namespace std;
     5 int maps[1001][1001];
     6 int ans;
     7 int main()
     8 {
     9     memset(maps,999999,sizeof(maps));
    10     int n,m;
    11     cin>>n>>m;
    12     int he,ta,len;
    13     for(int i=1;i<=m;i++)
    14     {
    15         cin>>he>>ta>>len;
    16         maps[ta][he]=maps[he][ta]=len;
    17     }
    18     int x,y;
    19     cin>>x>>y;
    20     for(int k = 1;k <= n;k++)
    21     for(int i = 1;i <= n;i++)
    22     for(int j = 1;j <= n;j++)
    23     {
    24         if(maps[i][j]>maps[i][k]+maps[k][j])
    25         maps[i][j]=maps[i][k]+maps[k][j];
    26     }
    27     
    28     printf("%d",maps[x][y]);
    29 }
    Floyed

    Dijkstra

    主要思想:每次找到一个能到达的最近的点,来更新到达其他点的距离

    思路:

    1.需要一个dis数组记录需要求的点到其他点的最短路径 pre数组记录前驱

    2.(1)初始化:dis[]=∞   dis[开始的点]=0  pre[开始的点]=0

     (2)<1>for(int i=1;i<=顶点总数;i++)

          找到dis[i]最小的点

          vis[找到的点]=1

          for(与找到的点相连的点)

          if(dis[find]+w[find][i]<dis[i])

          {

            1.松弛

            2.pre[i]=find 记录前驱

         }

    end

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<algorithm>
     4 #include<cstring>
     5 using namespace std;
     6 int n,m;
     7 const int maxn=9999999;
     8 int map[1001][1001],start,end;
     9 bool vis[1001];
    10 int dis[1001],road[1001],minn,k;
    11 
    12 void add(int now)
    13 {
    14        for(int i=1;i<=n;i++)
    15     {
    16         dis[i]=map[now][i];
    17     }
    18     vis[now]=true;
    19     dis[now]=0;
    20     for(int i=1;i<=n-1;i++)
    21     {
    22         minn=maxn;
    23         k=now;
    24         for(int j=1;j<=n;j++)
    25         if(vis[j]==false&&dis[j]<minn)
    26         {
    27             minn=dis[j];
    28             k=j;
    29         }
    30         vis[k]=true;
    31         for(int g=1;g<=n;g++)
    32         if(vis[g]==false&&dis[g]>dis[k]+map[k][g])
    33         {
    34             dis[g]=dis[k]+map[k][g];
    35         }
    36     }
    37     printf("%d",dis[end]);
    38     return ;
    39 
    40 }
    41 int main()
    42 {
    43     memset(map,maxn,sizeof(map));
    44     memset(vis,false,sizeof(vis));
    45     scanf("%d%d",&n,&m);
    46     int he,ta,len;
    47     for(int i=1;i<=m;i++)
    48     {
    49         scanf("%d%d%d",&he,&ta,&len);
    50         map[ta][he]=map[he][ta]=len;
    51     }
    52     memset(dis,maxn,sizeof(dis));
    53     cin>>start>>end;
    54     add(start);
    55     return 0;
    56 }
    Dijkstra

     Kruskal算法:

    主要思想:

    Kruskal算法将一个连通块当做一个集合。Kruskal首先将所有的边按从小到大顺序排序(一般使用快排),并认为每一个点都是孤立的,分属于n个独立的集合。然后按顺序枚举每一条边。如果这条边连接着两个不同的集合,那么就把这条边加入最小生成树,这两个不同的集合就合并成了一个集合;如果这条边连接的两个点属于同一集合,就跳过。直到选取了n-1条边为止。
     
    思路:
    算法描述:
    1.初始化并查集。father[x]=x。
    2.tot=0
    3.将所有边用快排从小到大排序。sort
    4.计数器 k=0;
    5.for (i=1; i<=M; i++)      //循环所有已从小到大排序的边
      if  这是一条u,v不属于同一集合的边(u,v)(因为已经排序,所以必为最小),
        begin
         ①合并u,v所在的集合,相当于把边(u,v)加入最小生成树。
         ②tot=tot+W(u,v)
          ③k++
          ④如果k=n-1,说明最小生成树已经生成,则break;
        end;
    6. 结束,tot即为最小生成树的总权值之和。
     1 #include<cstdio>
     2 #include<algorithm>
     3 #include<iostream>
     4 using namespace std;
     5 struct node{
     6     int x;
     7     int y;
     8     int v;
     9     bool operator < (const node &aa)const
    10     {
    11         if(v<aa.v)return 1;
    12         else return 0;    
    13     }
    14 }a[1001];
    15 int father[10001];
    16 int find(int x)
    17 {
    18     if(father[x]!=x) father[x]=find(father[x]);
    19     return father[x];
    20 }
    21 void unionn(int x,int y)
    22 {
    23     if(find(x)!=find(y))father[find(x)]=father[find(y)];
    24 }
    25 int num=0;
    26 int main()
    27 {
    28     int n,m=0,x;
    29     scanf("%d",&n);
    30     for(int i=1;i<=n;i++)
    31     for(int j=1;j<=n;j++)
    32     {
    33         cin>>x;
    34         if(x!=0)
    35         {
    36             m++;
    37             a[++num].x=i;
    38             a[num].y=j;
    39             a[num].v=x;
    40         }
    41     }
    42     int tot=0,k=0;
    43     for(int i=1;i<=n;i++)
    44     father[i]=i;
    45     sort(a+1,a+m+1);
    46     for(int i=1;i<=m;i++)
    47     {
    48         if(find(a[i].x)!=find(a[i].y))
    49         {
    50             unionn(a[i].x,a[i].y);
    51             tot+=a[i].v;
    52             k++;
    53         }
    54         if(k==n-1)break;
    55     }
    56     cout<<tot<<endl;
    57     return 0;
    58 }
    kruskal
  • 相关阅读:
    JVM描述符标识字符含义
    finally代码块的执行
    职业发展心之某日与领导的聊天
    springboot由于bean加载顺序导致的问题
    springboot自动配置国际化失效分析
    springboot 2.1.3.RELEASE添加filter,servlet源码学习
    jquery 数组添加不重复数据
    Oracle执行存储过程报错——ora-01031:权限不足
    oracle 存储过程使用动态sql
    将jsp页面的<s:iterator>的数据返回到action
  • 原文地址:https://www.cnblogs.com/sssy/p/6719305.html
Copyright © 2011-2022 走看看