zoukankan      html  css  js  c++  java
  • 【*篇】浅谈各种邻接表

    首先注:下面出现的代码完全没经编译,不保证编译成功,大家当伪代码看较好~

    在OIer的世界里,有一种奇妙的东西,叫图论。。
    而对于计算机,我们并不能将一张图输入,而需要一些存图方式
    比如下面这张图(画得不好看勿喷~):
    这里写图片描述
    最简单的存图方式是邻接矩阵
    *在一个n*n的矩阵中,第(i,j)个格子的值表示从i到j这条边的边权。。
    上图用邻接矩阵表示就是:

    -0123456
    0 0 3 0 5 0 0 0
    1 3 0 2 0 0 0 0
    2 0 2 0 2 0 0 0
    3 5 0 2 0 3 5 0
    4 0 0 0 3 0 2 0
    5 0 0 0 5 2 0 8
    6 0 0 0 0 0 8 0

    图的储存结构:

    int tu[N][N]; 

    建边:

    inline void build(int x,int y,int z){ //建从x到y边权为z的边
        tu[x][y]=z; //单向边
    #ifdef DOUBLE_EDGE
        tu[y][x]=z; //双向边
    #endif
    }

    枚举边:

    int x=start;
    for(int i=0;i<n;i++)
        if(tu[x][i]){ //若不为0表示相连
            int y=i; //跑到i的点
        }

    这个东西有个显而易见的缺点,O(n^2)的效率就决定了无法处理任何大规模的数据。。
    而O(nlogn)的算法中的n基本都在10^5级别。。数组都开不开啊_ (:з」∠) _

    所以我们需要邻接表,这样时间、空间效率都被控制在O(m)级别(m为边数)
    正统的邻接表是用指针……
    邻接表对于每个点主要保存了以下东西:
    - 这个点出发的第一条边
    - 每条边(和下面的都指该点出发的边)所指向的点
    - 每条边的边权(data域)
    - 每条边指向的下一条边
    储存的结构基本上是这样的:

    struct edge{
        int to,data;
        etype *next,*pair; //pair为反向边,视情况而建
        edge(){}
        edge(int t,int d,edge* nxt):to(t),data(d),next(nxt){}
        //dalao们发现C++的new实在是太慢了,所以要自己写一个orz
        void* operator new(unsigned,void* p){return p;}
    }*e[N];
    /*还有一个*/int Pe=0; //记录了边的总数

    建边:

    void build(int x,int y,int z){
        e[x]=new(Pe++)edge(y,z,e[x]); //指向y,边权z,上一条边是上一个e[x]..
    #ifdef DOUBLE_EDGE
        e[y]=new(Pe++)edge(x,z,e[y]);
        e[x]->pair=e[y];
        e[y]->pair=e[x];
    #endif
    }

    枚举边:

    int x=start;
    for(etype *i=e[no];i;i=i->next)
        int y=i->to;

    嗯,很好,我们现在有了存图的利器
    但是,指针的特性决定了对我们精确的要求。。
    不禁想起了指针调试的恐惧
    所以,我们应该是有更简(ju)便(ruo)的方法的:
    比如,数组模拟……(这应该也是应用最广泛的吧……)
    于是存储结构变成了:

    struct edge{
        int to,next,data;
    }e[M]; int v[N],tot=0; //v数组就是很多很多dalao的first数组,存点i出发的第一条边备查
    //网络流图中tot为了异或找反向边容易可以置为1..

    建边:

    void build(int x,int y,int z){
        e[++tot].to=y; e[tot].next=v[x]; e[tot].data=z; v[x]=tot;
    #ifdef DOUBLE_EDGE
        e[++tot].to=x; e[tot].next=v[y]; e[tot].data=z; v[y]=tot;
    #endif
    }

    枚举边:

    int x=start;
    for(int i=v[x];i;i=e[i].edge)
        int y=e[i].to;

    对就是这样。。
    其实似乎写到这里就可以结束了。。
    但是其实还是有一种写法的。。
    比如,C++的盆友们,还记得你们的STL么?
    vector也可以用来存邻接表哦(下面我给出没有边权的)~
    (而且似乎省选开O2什么的STL会格外跑得快)
    存储结构?

    vector<int> e[N];

    建边:

    void build(int x,int y){
        e[x].push_back(y); //媲美邻接矩阵的简便..
    #ifdef DOUBLE_EDGE
        e[y].push_back(x);
    #endif
    }

    枚举边:

    int x=start;
    for(/*unsigned*/int i=0;i<e[x].size();i++){ //不想要warning的可以加unsigned..
        int y=e[x][i];
    }

    行了,就介绍这么多。。。
    希望对大家有帮助,希望大家用的开心~~

  • 相关阅读:
    self 和 super 关键字
    NSString类
    函数和对象方法的区别
    求两个数是否互质及最大公约数
    TJU Problem 1644 Reverse Text
    TJU Problem 2520 Quicksum
    TJU Problem 2101 Bullseye
    TJU Problem 2548 Celebrity jeopardy
    poj 2586 Y2K Accounting Bug
    poj 2109 Power of Cryptography
  • 原文地址:https://www.cnblogs.com/enzymii/p/8412153.html
Copyright © 2011-2022 走看看