zoukankan      html  css  js  c++  java
  • 数据结构复习之【图】

    数据结构复习之【图】 - Java开发笔记 - 青藤园

    数据结构复习之【图】

    一、基本术语

    :由有穷、非空点集和边集合组成,简写成G(V,E);

    Vertex:图中的顶点;

    无向图:图中每条边都没有方向;

    有向图:图中每条边都有方向;

    无向边:边是没有方向的,写为(a,b)

    有向边:边是有方向的,写为<a,b>

    有向边也成为弧;开始顶点称为弧尾,结束顶点称为弧头;

    简单图:不存在指向自己的边、不存在两条重复的边的图;

    无向完全图:每个顶点之间都有一条边的无向图;

    有向完全图:每个顶点之间都有两条互为相反的边的无向图;

    稀疏图:边相对于顶点来说很少的图;

    稠密图:边很多的图;

    权重:图中的边可能会带有一个权重,为了区分边的长短;

    :带有权重的图;

    :与特定顶点相连接的边数;

    出度、入度:对于有向图的概念,出度表示此顶点为起点的边的数目,入度表示此顶点为终点的边的数目;

    :第一个顶点和最后一个顶点相同的路径;

    简单环:除去第一个顶点和最后一个顶点后没有重复顶点的环;

    连通图:任意两个顶点都相互连通的图;

    极大连通子图:包含竟可能多的顶点(必须是连通的),即找不到另外一个顶点,使得此顶点能够连接到此极大连通子图的任意一个顶点;

    连通分量:极大连通子图的数量;

    强连通图:此为有向图的概念,表示任意两个顶点a,b,使得a能够连接到b,b也能连接到a 的图;

    生成树:n个顶点,n-1条边,并且保证n个顶点相互连通(不存在环);

    最小生成树:此生成树的边的权重之和是所有生成树中最小的;

    AOV网:结点表示活动的网;

    AOE网:边表示活动的持续时间的网;

    二、图的存储结构

    1.邻接矩阵

    维持一个二维数组,arr[i][j]表示i到j的边,如果两顶点之间存在边,则为1,否则为0;

    维持一个一维数组,存储顶点信息,比如顶点的名字;

    下图为一般的有向图:

    注意:如果我们要看vi节点邻接的点,则只需要遍历arr[i]即可;

    下图为带有权重的图的邻接矩阵表示法:

     

    缺点:邻接矩阵表示法对于稀疏图来说不合理,因为太浪费空间; 

    2.邻接表

    如果图示一般的图,则如下图:

     如果是网,即边带有权值,则如下图:

    3.十字链表

    只针对有向图;,适用于计算出度和入度;

    顶点结点:

    边结点:

     

    好处:创建的时间复杂度和邻接链表相同,但是能够同时计算入度和出度;

    4.邻接多重表

    针对无向图; 如果我们只是单纯对节点进行操作,则邻接表是一个很好的选择,但是如果我们要在邻接表中删除一条边,则需要删除四个顶点(因为无向图);

    在邻接多重表中,只需要删除一个节点,即可完成边的删除,因此比较方便;

    因此邻接多重表适用于对边进行删除的操作;

    顶点节点和邻接表没区别,边表节点如下图:

    比如:

     

    5.边集数组

    合依次对边进行操作;

    存储边的信息,如下图:

    三、图的遍历

    DFS

    思想:往深里遍历,如果不能深入,则回朔;

    比如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    /**
     * O(v+e)
     */
    @Test
    public void DFS() {
        for (int i = 0; i < g.nodes.length; i++) {
            if (!visited[i]) {
                DFS_Traverse(g, i);
            }
        }
    }
     
    private void DFS_Traverse(Graph2 g, int i) {
        visited[i] = true;
        System.out.println(i);
        EdgeNode node = g.nodes[i].next;
        while (node != null) {
            if (!visited[node.idx]) {
                DFS_Traverse(g, node.idx);
            }
            node = node.next;
        }
    }

    BFS

    思想:对所有邻接节点遍历;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    <span style="white-space:pre;"> </span>/**
         * O(v+e)
         */
        @Test
        public void BFS() {
            ArrayList<Integer> list = new ArrayList<Integer>();
            for (int i = 0; i < g.nodes.length; i++) {
                if (!visited[i]) {
                    visited[i] = true;
                    list.add(i);
                    System.out.println(i);
                    while (!list.isEmpty()) {
                        int k = list.remove(0);
                        EdgeNode current = g.nodes[k].next;
                        while (current != null) {
                            if (!visited[current.idx]) {
                                visited[current.idx] = true;
                                System.out.println(current.idx);
                                list.add(current.idx);
                                 
                            }
                            current = current.next;
                        }
                    }
     
                }
            }
        }

    四、最小生成树

    prim

    邻接矩阵存储;
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    <span style="white-space:pre;"> </span>/**
         * 时间复杂度为O(n^2)
         * 适用于稠密图
         */
        @Test
        public void prim(){
            int cost[] = new int[9];
            int pre[] = new int[9];
             
            for(int i=0;i<g1.vertex.length;i++){
                cost[i] = g1.adjMatrix[0][i];
            }
            cost[0] = 0;
             
            for(int i=1;i<g1.vertex.length;i++){
                int min = 65536;
                int k = 0;
                for(int j=1;j<g1.vertex.length;j++){
                    if(cost[j]!=0&&cost[j]<min){
                        min = cost[j];
                        k = j;
                    }
                }
                cost[k] = 0;
                System.out.println(pre[k]+","+k);
                for(int j=1;j<g1.vertex.length;j++){
                    if(cost[j]!=0&&g1.adjMatrix[k][j]<cost[j]){
                        pre[j] = k;
                        cost[j] = g1.adjMatrix[k][j];
                    }
                }
            }
        }

    krustral

    边集数组存储;
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    <span style="white-space:pre;"> </span>/**
         * 时间复杂度:O(eloge)
         * 适用于稀疏图
         */
        @Test
        public void krustral(){
            Edge[] edges = initEdges();
            int parent[] = new int[9];
            for(int i=0;i<edges.length;i++){
                Edge edge = edges[i];
                int m = find(parent,edge.begin);
                int n = find(parent,edge.end);
                if(m!=n){
                    parent[m] = n;
                    System.out.println(m+","+n);
                }
            }
             
        }
        private static int find(int[] parent, int f) {
            while (parent[f] > 0) {
                f = parent[f];
            }
            return f;
        }

    五、最短路径

    dijkstra算法

    邻接矩阵存储;
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    <span style="white-space:pre;"> </span>//O(n^2)
        @Test
        public void Dijkstra(){
            int distance[] = new int[9];
            int pre[] = new int[9];
            boolean finished[] = new boolean[9];
            finished[0] = true;
            for(int i=0;i<9;i++){
                distance[i] = g1.adjMatrix[0][i];
            }
            int k = 0;
            for(int i=1;i<9;i++){
                int min = 65536;
                for(int j=0;j<9;j++){
                    if(!finished[j]&&distance[j]<min){
                        min = distance[j];
                        k = j;
                    }
                }
                finished[k] = true;
                System.out.println(pre[k]+","+k);
                for(int j=1;j<9;j++){
                    if(!finished[j]&&(min+g1.adjMatrix[k][j])<distance[j]){
                        distance[j] = min+g1.adjMatrix[k][j];
                        pre[j] = k;
                    }
                }
            }
        }

    Floyd

    使用:
    (1)邻接矩阵:存储图;
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    <span style="white-space:pre;"> </span>/**
         * O(n^3)
         * 求出任意顶点之间的距离
         */
        @Test
        public void floyd(Graph1 g) {
            int i, j, k;
            int length = g.vertex.length;
            int dist[][] = new int[length][length];
            int pre[][] = new int[length][length];
            for (i = 0; i < g.vertex.length; i++) {
                for (j = 0; j < g.vertex.length; j++) {
                    pre[i][j] = j;
                    dist[i][j] = g.adjMatrix[i][j];
                }
            }
            for (i = 0; i < length; i++) {
                for (j = 0; j < g.vertex.length; j++) {
                    for (k = 0; k < g.vertex.length; k++) {
                        if (dist[i][j] > dist[i][k] + dist[k][j]) {
                            dist[i][j] = dist[i][k] + dist[k][j];
                            pre[i][j] = pre[i][k];
                        }
                    }
                }
     
            }
            System.out.println();
        }

    六、拓扑排序

    使用数据结构:

    (1)栈:用来存放入度为0的节点;

    (2)变种邻接列表:作为图的存储结构;此邻接列表的顶点节点还需要存放入度属性;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    /**
    * O(n+e)
    */
    private static String topologicalSort(Graph2 g2) {
            Stack<Integer> s = new Stack<Integer>();
            int count = 0;
            for(int i=0;i<g2.nodes.length;i++){
                if(g2.nodes[i].indegree==0){
                    s.push(i);
                }
            }
            while(!s.isEmpty()){
                int value = s.pop();
                System.out.println(value+"、");
                count++;
                EdgeNode node = g2.nodes[value].next;
                while(node!=null){
                    g2.nodes[node.idx].indegree--;
                    if(g2.nodes[node.idx].indegree==0){
                        s.push(node.idx);
                    }
                    node = node.next;
                }
                 
            }
            if(count<g2.nodes.length){
                return "error";
            }
            return "ok";
        }

    七、关键路径

    使用数据结构:
    (1)变种邻接列表:同拓扑排序;
    (2)变量:
    ltv表示某个事件的最晚开始时间;
    etv表示事件最早开始时间;
    ete表示活动最早开始时间;
    lte表示活动最晚开始时间;
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    <span style="white-space:pre;"> </span>//O(n+e)<span style="white-space:pre;">  </span>@Test
        public void CriticalPath(){
             
            Stack<Integer> stack = topological_etv();
            int length = stack.size();
            if(stack==null){
                return ;
            }
            else{
                int[]ltv = new int[length];
                for(int i=0;i<stack.size();i++){
                    ltv[i] = etv[stack.size()-1];
                }
                //从拓扑排序的最后开始计算ltv
                while(!stack.isEmpty()){
                    int top = stack.pop();
                    EdgeNode current = g.nodes[top].next;
                    while(current!=null){
                        int idx = current.idx;
                        //最晚发生时间要取所有活动中最早的
                        if((ltv[idx]-current.weight)<ltv[top]){
                            ltv[top] = ltv[idx]-current.weight;
                        }
                    }
                }
                int ete = 0;
                int lte = 0;
                for(int j=0;j<length;j++){
                    EdgeNode current = g.nodes[j].next;
                    while(current!=null){
                        int idx = current.idx;
                        ete = etv[j];
                        lte = ltv[idx]-current.weight;
                        if(ete==lte){
                            //是关键路径
                        }
                    }
                }
                 
            }
             
             
        }
        private Stack<Integer> topological_etv(){
            Stack<Integer> stack2 = new Stack<Integer>();
            Stack<Integer>stack1 = new Stack<Integer>();
            for(int i=0;i<g.nodes.length;i++){
                if(g.nodes[i].indegree==0){
                    stack1.add(i);
                }
            }
            etv[] = new int[g.nodes.length];
            int count = 0;
            while(!stack1.isEmpty()){
                int top = stack1.pop();
                count++;
                stack2.push(top);
                 
                EdgeNode current = g.nodes[top].next;
                while(current!=null){
                    int idx = current.idx;
                    if((--g.nodes[idx].indegree)==0){
                        stack1.push(idx);
                    }
                    if((etv[top]+current.weight)>etv[idx]){
                        etv[idx] = etv[top]+current.weight;
                    }
                    current = current.next;
                }
            }
            if(count<g.nodes.length){
                return null;
            }
            return stack2;
        }
    来源:http://blog.csdn.net/xiazdong/article/details/7354411
  • 相关阅读:
    Flexigrid在IE下不显示数据的处理
    [置顶] ios 网页中图片点击放大效果demo
    WPF仿360卫士9.0界面设计
    Android调用相机并将照片存储到sd卡上
    Android 将文件保存到SD卡,从卡中取文件,及删除文件
    java 正则表达式学习
    linux下的块设备驱动(一)
    已知用经纬度表示的两点,求两点之间的直线距离
    IndiaHacks 2016
    IndiaHacks 2016
  • 原文地址:https://www.cnblogs.com/lexus/p/2526354.html
Copyright © 2011-2022 走看看