数据结构基础(五)图以及图的遍历

概念

定义

图是一种较线性表和树更为复杂的数据结构
相较于线性表的一对一(每个结点只有一个前驱后驱)和树的一对多(层级结构,上层结点可以与多个下层结点相关),图形结构的元素关系是多对多(即结点之间的关系可以是任意的)

图可分为有向图和无向图

术语

连通图:对于无向图,如果任意两个结点之间都是通的,则称之为连通图
连通分量:对于无向非连通图,极大连通子图称为其连通分量
强连通图:对于有向图,任意两个结点有路径
强连通分量:对于有向图非连通图,极大连通子图称为其强连通分量
连通图的生成树:该图的极小连通子图,它含有图中全部n个顶点和只有足以构成一棵树的n-1条边,称为图的生成树


例如上图a中无向图G3,图b便为其三个连通分量


上图为连通分量图的生成树

图的存储结构

图的常用的存储结构有:邻接矩阵邻接链表十字链表邻接多重表边表,其中邻接矩阵和邻接链表是较常用的表示方法。

邻接矩阵

即用一维数组放置其顶点,二维数组放置顶点之间的关系
例如对于无向图,A[i][j]=0表示i结点和j结点之间不连通,A[i][j]=1表示连通;对于有向图,A[i][j]表示i结点和j结点之间的权值。
该二维数组又称为邻接矩阵

邻接链表

即用一个数组存储所有顶点,而每个顶点有一个域指向一个单链表,该单链表存储该顶点所有邻接顶点及其相关信息。



如上图,头结点(顶点)中data存储该顶点相关信息,firstarc存储单链表第一个结点的位置;
表结点(链表结点)中adjvex存储该领接顶点位置,nextarc存储链表下一个结点位置,info存储边相关信息(例如有向图中的权值)
下图为邻接链表的图解


20130708131719421.png

实现代码

给出手动实现的邻接矩阵代码:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 弧
 */
class Arc{
    //弧连接的两个顶点
    Object v1,v2;
    //图:-1 无连接  >=0有连接
    int Type;
    //弧的信息
    String info;
    
    public Arc() {}

    public Arc(Object v1, Object v2, int type, String info) {
        super();
        this.v1 = v1;
        this.v2 = v2;
        Type = type;
        this.info = info;
    }
    
    @Override
    public String toString() {
        return v1+" "+v2+" "+Type+" "+info;
    }
    
}

/**
 * 领接矩阵图
 */
public class MatrixGraph {
    /**默认顶点最大个数*/
    final static int defaultSize=10;
    /**顶点集合*/
    List<Object> vertexs;
    /**边的关系矩阵*/
    Arc[][] arcs;
    /**最大顶点个数*/
    int maxLen;
    /**弧的个数*/
    int arcNum;
    
    
    /**
     * 默认构造函数
     */
    public MatrixGraph() {
        this.maxLen = defaultSize;
        this.vertexs = new ArrayList<Object>(maxLen);
        this.arcs = new Arc[maxLen][maxLen];
        this.arcNum = 0;
    }
    
    /**
     * 带参构造函数
     * @param vers 顶点数组
     */
    public MatrixGraph(Object[] vers) {
        this.maxLen=vers.length;
        this.vertexs=new ArrayList<Object>(Arrays.asList(vers));
        this.arcs = new Arc[maxLen][maxLen];
        this.arcNum = 0;
    }
    
    /**
     * 添加顶点
     * @param v
     */
    public void addVertex(Object v) {
        vertexs.add(v);
        if(vertexs.size()>maxLen)
            extendSize();
    }
    
    /**
     * 扩展最大顶点个数和领接矩阵
     */
    public void extendSize() {
        maxLen*=2;
        arcs=Arrays.copyOf(arcs, maxLen);
    }
    
    /**
     * 添加一条弧
     * @param a
     * @param b
     */
    public void addArc(Object a,Object b) {
        addArc(a, b, 0);
    }
    
    /**
     * 添加一条带权弧
     * @param a
     * @param b
     * @param weight
     */
    public void addArc(Object a,Object b,int weight) {
        int i=vertexs.indexOf(a);
        int j=vertexs.indexOf(b);
        
        if(i==-1||j==-1){
            throw new ArrayIndexOutOfBoundsException("该顶点不存在"); 
        }
        
        arcs[i][j]=new Arc(a, b, weight, "");
        arcNum++;
        
    }
    
    /**
     * 删除a到b的弧
     * @param a
     * @param b
     */
    public void removeArc(Object a,Object b) {
        int i=vertexs.indexOf(a);
        int j=vertexs.indexOf(b);
        if(i==-1||j==-1){
            throw new ArrayIndexOutOfBoundsException("该顶点不存在"); 
        }
        
        if(arcs[i][j]!=null){
            arcs[i][j]=null;
            arcNum--;           
        }
        else {
            System.out.println("该弧已经不存在");
        }
    }
    
    /**
     * 删除一个顶点
     * @param a
     */
    public void removeVertexs(Object a) {
        int i=vertexs.indexOf(a);
        
        if(i==-1)
            throw new ArrayIndexOutOfBoundsException("该顶点不存在");
        vertexs.remove(i);
        //重新生成邻接矩阵
        int length=arcs.length;
        for (int j = 0; j < length-1; j++) {
            for (int z = 0; z < length-1; z++) {
                if(z>=i)
                    arcs[j][z]=arcs[j][z+1];
            }
            arcs[j][length-1]=null;
            if(j>=i)
                arcs[j]=arcs[j+1];
        }
        arcs[length-1]=new Arc[length];
    }
    
    /**
     * 清空图
     */
    public void clear() {
        vertexs=null;
        arcs=null;
        arcNum=0;
        maxLen=defaultSize;
    }
    
    /**
     * 打印图的信息
     */
    public void printGraph() {
        for (int i = 0; i < arcs.length; i++) {
            for (int j = 0; j < arcs[i].length; j++) {
                if(arcs[i][j]!=null)
                System.out.println(arcs[i][j]);
            }
        }
    }
    
    public static void main(String[] args) {
        Object obj[] = { 'A', 'B', 'C', 'D', 'E', 'F' };  
        
        MatrixGraph graph = new MatrixGraph(obj);  
  
        graph.addArc('A','C',5);  
        graph.addArc('B','A',2);  
        graph.addArc('C','B',15);  
        graph.addArc('E','D',4);  
        graph.addArc('F','E',18);  
        graph.addArc('A', 'F', 60);  
        graph.addArc('C', 'F', 70); 
        
        graph.printGraph();
        System.out.println("--------------");
        graph.removeVertexs('A');
        graph.printGraph();
        System.out.println("--------------");
        graph.removeArc('C', 'B');
        graph.printGraph();
    }
    
}/**Output:
A C 5 
A F 60 
B A 2 
C B 15 
C F 70 
E D 4 
F E 18 
--------------
C B 15 
C F 70 
E D 4 
F E 18 
--------------
C F 70 
E D 4 
F E 18 */

图的遍历

图的遍历有两种:DFS(Deep First Search)和BFS(Breadth First Search)
这两个算法算是我在ACM呆的时候做题最多的算法了。

DFS(深度优先算法)

即每次遍历时偏向纵深方向搜索,类似树的先序遍历

递归实现:

  1. 访问顶点v;visited[v]=1;//算法执行前visited[n]=0
  2. w=顶点v的第一个邻接点;
  3. while(w存在)
    if(w未被访问)
    从顶点w出发递归执行该算法;
    w=顶点v的下一个邻接点;

非递归实现:

  1. 栈S初始化;visited[n]=0;
  2. 访问顶点v;visited[v]=1;顶点v入栈S
  3. while(栈S非空)
    x=栈S的顶元素(不出栈);
    if(存在并找到未被访问的x的邻接点w)
    访问w;visited[w]=1;
    w进栈;
    else
    x出栈;

BFS(广度优先算法)

即每次时遍历分层搜索,先搜索完每个顶点的领接结点,再对领接结点重复这一步。

非递归实现

  1. 初始化队列Q;visited[n]=0;
  2. 访问顶点v;visited[v]=1;顶点v入队列Q;
  3. while(队列Q非空)
    v=队列Q的对头元素出队;
    w=顶点v的第一个邻接点;
    while(w存在)
    如果w未访问,则访问顶点w;
    visited[w]=1;
    顶点w入队列Q;
    w=顶点v的下一个邻接点。

下面是具体的代码

/**
     * 深度优先遍历
     * @param o
     * @return
     */
    public String dfs(Object o) {
        StringBuilder result=new StringBuilder();
        Stack<Object> stack=new Stack<Object>();
        //访问标记数组
        Boolean[] visit=new Boolean[vertexs.size()];
        //初始化所有顶点为未访问
        for (int i = 0; i < visit.length; i++) {
            visit[i]=false;
        }
        //放入起始结点入栈
        stack.push(o);
        visit[vertexs.indexOf(o)]=true;
        result.append(o);
        //利用栈进行深度优先遍历
        while (!stack.isEmpty()) {
            //访问栈的顶点
            Object out=stack.peek();
            Object next=getNext(out, visit);
            if(next!=null){
                stack.push(next);
                visit[vertexs.indexOf(next)]=true;
                result.append("->"+next);
            }
            else{
                stack.pop();
            }
        }
        
        return result.toString();
    }
    
    /**
     * 广度优先遍历
     * @param o
     * @return
     */
    public String bfs(Object o) {
        StringBuilder result = new StringBuilder();
        Queue queue = new LinkedList<Object>();
        // 访问标记数组
        Boolean[] visit = new Boolean[vertexs.size()];
        // 初始化所有顶点为未访问
        for (int i = 0; i < visit.length; i++) {
            visit[i] = false;
        }
        //放入起始结点入队列
        queue.add(o);
        visit[vertexs.indexOf(o)]=true;
        result.append(o);
        //利用队列进行广度优先遍历
        while (!queue.isEmpty()) {
            //访问堆顶点
            Object out=queue.peek();
            Object next=getNext(out, visit);
            if(next!=null){
                queue.add(next);
                visit[vertexs.indexOf(next)]=true;
                result.append("->"+next);
            }else{
                queue.poll();
            }   
        }
        return result.toString();
    }
    
    /**
     * 获取o结点的下一个未被访问的领接结点
     * @param o
     * @param visit
     * @return
     */
    private Object getNext(Object o,Boolean[] visit){
        
        int index=vertexs.indexOf(o);
        for (int j = 0; j < arcs.length; j++) {
            if(arcs[index][j]!=null&&visit[j]==false)
                return vertexs.get(j);
        }   
        return null;
    }

参考
图(2)—— 邻接矩阵表示法
图(3)——邻接链表法
深度优先遍历与广度优先遍历

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容

  • 第一章 绪论 什么是数据结构? 数据结构的定义:数据结构是相互之间存在一种或多种特定关系的数据元素的集合。 第二章...
    SeanCheney阅读 5,712评论 0 19
  • -DFS(Depth First Search):深度优先搜索 访问完一个顶点的所有邻接点之后,会按原路返回,对应...
    Spicy_Crayfish阅读 2,807评论 1 0
  • VisuAlgo!一,Date Structure的核心技术是分解和抽象二,基本概念和常用术语 三,逻辑结构1,逻...
    斜杠青年许晏铭阅读 845评论 0 0
  • https://zh.visualgo.net/graphds 浅谈图形结构https://zh.visualgo...
    狼之独步阅读 4,071评论 0 0
  • 第一次接触萧红是在图书馆,那是一本红色的小书,忘记当时是怎样的心情,莫名其妙手伸到那里就把它拿下来。拿它的时候我并...
    小红丫头阅读 416评论 3 11