• 无向图-笔记-代码


    //API:
    public
    interface Graph { int V();//顶点数 int E();//边数 void addEdge(int v, int w);//添加一条边 Iterable<Integer> adj(int v); //某个顶点v的相邻顶点 String toString(); }

    用 邻接表数组 实现的图

    public class AdjacencyListArrayGraph implements Graph {
        int v;
        int e;
    
        IntLinked[] adjList;
        //2.优化成 key:顶点 val:相邻定点 的 Hash表(如红黑树,或拉链散列表),但为了集中精力在本质的图的特性上,不用过于麻烦的优化方法。
    
    
        public AdjacencyListArrayGraph(int v) {
            this.v = v;
            e = 0;
            adjList = new IntLinked[v];
            for (int i = 0; i < v; i++) {
                adjList[i] = new IntLinked();
            }
        }
    
        @Override
        public int V() {
            return v;
        }
    
        @Override
        public int E() {
            return e;
        }
    
        @Override
        public void addEdge(int v, int w) {
            adjList[v].add(w);
            adjList[w].add(v);
            e++;
        }
    
        public String toString() {
            String res = "";
            for (int i = 0; i < v; i++) {
                res += "[" + i + "] ";
                Iterable<Integer> it = new ALAGIntIterable(adjList[i].head);
                Iterator<Integer> itr = it.iterator();
                while (itr.hasNext()) {
                    int to = itr.next();
                    res += to + " ";
                }
                res += "
    ";
            }
            return res;
        }
    
        @Override
        public Iterable<Integer> adj(int v) {
            return new ALAGIntIterable(adjList[v].head);
        }
    
        public class ALAGIntIterable implements Iterable<Integer> {
            IntNode node;
            public ALAGIntIterable(IntNode node) {
                this.node = node;
            }
    
            @Override
            public Iterator<Integer> iterator() {
                return new IntNodeIterator(node);
            }
    
            @Override
            public void forEach(Consumer<? super Integer> action) {
    
            }
    
            @Override
            public Spliterator<Integer> spliterator() {
                return null;
            }
    
            public class IntNodeIterator implements Iterator<Integer> {
                IntNode node;
                public IntNodeIterator(IntNode node){
                    this.node = node;
                }
                @Override
                public boolean hasNext() {
                    return node != null;
                }
    
                @Override
                public Integer next() {
                    IntNode tmp = node;
                    node = node.n;
                    return tmp.k;
                }
            }
        }
    
        public static class ALAGBuilder {
            File f;
    
            public ALAGBuilder(File f) {
                this.f = f;
            }
    
            public AdjacencyListArrayGraph build() {
                FileInputStream fis = null;
                BufferedReader br = null;
                AdjacencyListArrayGraph g = null;
                int v = 0, e = 0;
                try {
                    fis = new FileInputStream(f);
                    br = new BufferedReader(new InputStreamReader(fis));
                    String txt = null;
    
                    txt = br.readLine();
                    if (txt != null) {
                        v = Integer.parseInt(txt); //读取顶点数量
                    }
                    txt = br.readLine();
                    if (txt != null) {
                        e = Integer.parseInt(txt); //读取边的数量 ,感到有些多余,直接把剩下的行遍历完就行了
                    }
                    g = new AdjacencyListArrayGraph(v);
                    int count = 0;
                    while ((txt = br.readLine()) != null) { //遍历剩下的行,加入边到图中,未作有效校验
                        String[] line = txt.split(" ");
                        int from = Integer.parseInt(line[0]);
                        int to = Integer.parseInt(line[1]);
                        g.addEdge(from, to);
                    }
                    return g;
                } catch (Exception e1) {
                    e1.printStackTrace();
                } finally {
                    if (fis != null) {
                        try {
                            fis.close();
                        } catch (IOException e1) {
                            e1.printStackTrace();
                        }
                    }
                    if (br != null) {
                        try {
                            br.close();
                        } catch (IOException e1) {
                            e1.printStackTrace();
                        }
                    }
                }
                return null;
            }
        }
    
        public static void main(String[] args) {
            Graph g = new AdjacencyListArrayGraph(10);
    
            File f = new File("D:\aa.txt");
            Graph g2 = new ALAGBuilder(f).build();
            System.out.println(g2.toString());
        System.out.println("degree 0 : " + GraphUtil.degree(g2, 0));
        System.out.println("maxDegree : " + GraphUtil.maxDegree(g2));
        System.out.println("avgDegree : " + GraphUtil.avgDegree(g2));
        System.out.println("numberOfSelfloops : " + GraphUtil.numberOfSelfloops(g2));

        //深度搜索
        Search search = new DeepFirstSearch(g2,2);
        System.out.println(" === Search");
        System.out.println("2 count : " + search.count());
        System.out.println("2 marked 3 : " + search.marked(3)); //2个顶点是否连通? = 2个顶点之间是否有至少一条路径存在?
        System.out.println("2 marked 1 : " + search.marked(1));

        //图中有多少个连通子图?
        //1.将第通过深度搜索出来的连通子图中的顶点,从图中删除,剩下去除掉第一个连通子图的剩余的子图 (suck)
        //2.从剩余的顶点中随机找一个顶点,再一次做深度搜索
        //3.重复步骤1,直到图中没有剩余的顶点为之,循环的次数就是整个图中连通子图的数量
        //TODO
    }

    aa.txt 的文件格式:

    顶点数量
    边数量
    顶点1 顶点2
    。。。
    。。。
    顶点x 顶点y

    头2行是一行一个单独的数字

    后面是记录 边的信息的,一行一条边

    aa.txt

    5
    8
    0 1
    0 2
    1 3
    1 4
    2 3
    2 4
    0 0
    1 1

    一些工具类

    public class GraphUtil {
        //
        public static int degree(Graph g, int v) {
            Iterable<Integer> it = g.adj(v);
            int count = 0;
            for (int n : it) {
                count++;
            }
            return count;
        }
    
        //所有定点最大度
        public static int maxDegree(Graph g) {
            int max = 0;
            for (int v = 0; v < g.V(); v++) {
                int d = degree(g, v);
                max = max < d ? d : max;
            }
            return max;
        }
    
        //所有定点平均度数
        public static double avgDegree(Graph g) {
            return 2.0 * g.E() / g.V();  // * 2 因为 一条边 被算了2次(出现在一对定点的各自的相邻顶点列表)
        }
    
        //图中自环的个数
        public static int numberOfSelfloops(Graph g) {
            int count = 0;
            for (int v = 0; v < g.V(); v++)
                for (int w : g.adj(v))
                    if (v == w) count++;
    
            return count / 2;  //因为addEdge 的实现,一个自环会被加2次到同一个定点上,所以除以2
        }
    }

    广度优先:

    public class DeepFirstSearch implements Search {
        int count;
        boolean[] marked; //可达的顶点=true
    
        public DeepFirstSearch(Graph g, int s) {
            marked = new boolean[g.V()];
            dfs(g, s);
        }
    
        private void dfs(Graph g, int v) {
            marked[v] = true;
            count++;
            for (int w : g.adj(v)) {
                if (!marked[w])
                    dfs(g, w);//递归,有栈的作用
            }
        }
    
        @Override
        public boolean marked(int v) {
            return marked[v];
        }
    
        @Override
        public int count() { //从 s点开始,连通的点的个数(不算自环)
            return count;
        }
    }

    小而强劲

    结果:

    [0] 1 2 0 0
    [1] 0 3 4 1 1
    [2] 0 3 4
    [3] 1 2
    [4] 1 2

    degree 0 : 4
    maxDegree : 5
    avgDegree : 3.2
    numberOfSelfloops : 2

    === Search
    2 count : 5
    2 marked 3 : true
    2 marked 1 : true

    图是一种数据结构,一些内容来自数学的图论研究成果。

    1.有/无向 (方向,单向)

    2。有/无权(权重)

    可组合出4种图,无向图,有向图,有权图,有向有权图

    这里的例子是无向图

    常见概念有:

    顶点,边,度,自环,连通图,平行边,稠密/稀疏图,子图,树,森林,无环图,二分图,连通子图,生成树森林等(挺多的)

    连通图:是一个整体,图中任意一点可以到达这个图上其他任意点

    生成树:只是连通图的一副子图

    生成树森林:一个图的所有连通子图的生成树集合

    深度搜索:

    遍历点的相邻点时,用栈作为临时存储结构(可以用递归实现)(优先处理起始点的第一个相邻点,然后再从该第一个相邻点作为新起始点)

    广度搜索:

    用队列(优先把起始点周围的一圈相邻点处理完)

    无向图, 环检测 ,假设不存在自环或平行边

    public class CycGraphTest { //无向图, 环检测 ,假设不存在自环或平行边
        Graph g;
        boolean[] marked; //是否已访问过
        boolean hasCyc;
    //    int[] fromVertex;
    
        public CycGraphTest(Graph g) {
            this.g = g;
            marked = new boolean[g.v()];
    //        fromVertex = new int[g.v()];
    //        Arrays.fill(fromVertex, -1);
            hasCyc = false;
    
            for (int s = 0; s < g.v(); s++) {
                if (!marked[s]) {
                    dfs(s, s);
                }
            }
        }
    
        private void dfs(int v, int fromV) {
            marked[v] = true;
    //        fromVertex[v] = fromV;
            for (Integer w : g.adj(v)) {
                if (!marked[w]) {
                    dfs(w, v);
                } else if (fromV != w) { //不是来的点(无向图),  // && fromVertex[w] != v  访问者也不是我(说明被从其他路径访问过)
                    hasCyc = true;
                }
            }
        }
    
        public boolean isHasCyc() {
            return hasCyc;
        }
    
        public static void main(String[] args) {
            Graph g = new Graph(6);
            g.addEdge(0, 1);
            g.addEdge(1, 2);
            g.addEdge(2, 0);
            g.addEdge(3, 4);
            System.out.println("Vertex : edgeTo");
            System.out.println(g.toString());
    
            CycGraphTest cc = new CycGraphTest(g);
            System.out.println("isHasCyc: " + cc.isHasCyc());
        }
    }

    个别大小写需要改一下。。。

    输出

    Vertex : edgeTo
    0 : 2 1 
    1 : 2 0 
    2 : 0 1 
    3 : 4 
    4 : 3 
    5 : 
    
    isHasCyc: true

    是否是二分图 (顶点只由2个颜色标记,且相邻的顶点必须不同色)

    public class ColorGraphTest { //是否是二分图 (顶点只由2个颜色标记,且相邻的顶点必须不同色)
        boolean[] colors;
        boolean[] marked;
        boolean isBinGraph;
        Graph g;
    
        public ColorGraphTest(Graph g) {
            this.g = g;
            colors = new boolean[g.v()];
            marked = new boolean[g.v()];
            isBinGraph = true;
    
            for (int s = 0; s < g.v(); s++) {
                if(!marked[s]) //跑完所有子图
                    dfs(s, s);
            }
        }
    
        public void dfs(int v, int fromV) {
            marked[v] = true;
            colors[v] = !colors[fromV];
            for (int w : g.adj(v)) {
                if (!marked[w]) {
                    dfs(w, v);
                } else if (colors[v] == colors[w]) { //存在相邻的2个已访问过的点 的颜色 一致,说明不是二分图
                    isBinGraph = false;
                    System.out.println("v-w " + v + "-" + w + " colors v-w  " + colors[v] + "-" + colors[w]);
                }
            }
        }
    
        public boolean isBinGraph() {
            return isBinGraph;
        }
    
        public static void main(String[] args) {
            Graph g = new Graph(6);
            g.addEdge(0, 1);
            g.addEdge(1, 2);
            g.addEdge(2, 0);
    
            g.addEdge(3, 4);
            System.out.println("Vertex : edgeTo");
            System.out.println(g.toString());
    
            ColorGraphTest ct = new ColorGraphTest(g);
            System.out.println("isBinGraph: " + ct.isBinGraph());
        }
    }
    Vertex : edgeTo
    0 : 2 1 
    1 : 2 0 
    2 : 0 1 
    3 : 4 
    4 : 3 
    5 : 
    
    v-w 1-0 colors v-w  true-true
    v-w 0-1 colors v-w  true-true
    isBinGraph: false

    连通图, 连通子图判断

    public class CCTest { //连通图, 连通子图判断
        Graph g;
        boolean[] marked; //是否已访问过
        int[] id; //每个顶点对应的连通图id
        int count; //连通图个数
    
        public CCTest(Graph g) {
            this.g = g;
            marked = new boolean[g.v()];
            id = new int[g.v()];
            count = 0;
    
            for (int s = 0; s < g.v(); s++) {
                if (!marked[s]) {
                    dfs(s);
                    count++;
                }
            }
        }
    
        private void dfs(int v) {
            marked[v] = true;
            id[v] = count;
            for (Integer w : g.adj(v)) {
                if (!marked[w]) {
                    dfs(w);
                }
            }
        }
    
        public int unionId(int v) {
            return id[v];
        }
    
        public int count() {
            return count;
        }
    
        public static void main(String[] args) {
            Graph g = new Graph(5);
            g.addEdge(0, 1);
            g.addEdge(1, 2);
            System.out.println("Vertex : edgeTo");
            System.out.println(g.toString());
    
            CCTest cc = new CCTest(g);
            System.out.println("unionCount : " + cc.count());
            System.out.println("Vertex : unionId");
            for (int v = 0; v < g.v(); v++)
                System.out.println(v + " : " + cc.unionId(v));
        }
    }
    Vertex : edgeTo
    0 : 1 
    1 : 2 0 
    2 : 1 
    3 : 
    4 : 
    
    unionCount : 3
    Vertex : unionId
    0 : 0
    1 : 0
    2 : 0
    3 : 1
    4 : 2

    广度优先,记录路径

    public class BFSPathTest { //广度优先,记录路径
        Graph g;
        boolean[] marked;
        int[] fromVertex;
        int s;
    
        public BFSPathTest(Graph g, int s) { //s 起点
            this.g = g;
            this.s = s;
            marked = new boolean[g.v()];
            fromVertex = new int[g.v()];
            Arrays.fill(fromVertex, -1);
            bfs(s);
        }
    
        //广度优先
        private void bfs(int s) {
            Queue<Integer> queue = new LinkedList();
            queue.add(s);
            marked[s] = true;
            fromVertex[s] = s;
            Integer n;
            while ((n = queue.poll()) != null) {
                System.out.println("v " + n);
                for (int w : g.adj(n)) {
                    if (!marked[w]) {
                        marked[w] = true;
                        fromVertex[w] = n;
                        System.out.println("add  +" + w);
                        queue.add(w);
                    }
                }
            }
        }
    
        public boolean hasPathTo(int v) {
            return marked[v];
            //fromVertex[v] != -1; 不用这个 因为不考虑其他连通子图,只考虑和s相关的连通图
        }
    
        public List<Integer> pathTo(int v) {
            List l = new ArrayList();
            if(!hasPathTo(v))
                return l;
    
            Stack stack = new Stack();
            stack.push(v);
            while (v != s) {
                stack.push(fromVertex[v]);
                v = fromVertex[v];
            }
    //        stack.push(s);
    
            while (!stack.empty()) {
                l.add(stack.pop());
            }
            return l;
        }
    
        public static void main(String[] args) {
            Graph g = new Graph(7);
            g.addEdge(0, 1);
            g.addEdge(1, 2);
            g.addEdge(2, 0);
            g.addEdge(3, 0);
    
            g.addEdge(4, 5);
            System.out.println("Vertex : edgeTo");
            System.out.println(g.toString());
    
            BFSPathTest pg = new BFSPathTest(g, 1);
            for (int v = 0; v < g.v(); v++) {
                System.out.println("hasPathTo from 1 to " + v + " :" + pg.hasPathTo(v));
                System.out.println("    pathTo " + pg.pathTo(v));
            }
        }
    }
    Vertex : edgeTo
    0 : 3 2 1 
    1 : 2 0 
    2 : 0 1 
    3 : 0 
    4 : 5 
    5 : 4 
    6 : 
    
    v 1
    add  +2
    add  +0
    v 2
    v 0
    add  +3
    v 3
    hasPathTo from 1 to 0 :true
        pathTo [1, 0]
    hasPathTo from 1 to 1 :true
        pathTo [1]
    hasPathTo from 1 to 2 :true
        pathTo [1, 2]
    hasPathTo from 1 to 3 :true
        pathTo [1, 0, 3]
    hasPathTo from 1 to 4 :false
        pathTo []
    hasPathTo from 1 to 5 :false
        pathTo []
    hasPathTo from 1 to 6 :false
        pathTo []

    在一个有V个顶点的图中,最多有几条边?

    (V*(V-1))/2 条

    V,每个顶点,与图中除了自己以外的顶点相连 有 V-1 条,每个这样的顶点都如此相连=V*(V-1), 去掉每条边重复了2次 ,除以2     =(V*(V-1)) / 2

    在一个有V个顶点的连通图中,最少有几条边?

    V-1 条

    最后抱怨一下(你要接受我的精神污染,你生存的意义在于被别人压榨):

    感觉自己已经失去独立思考,自主自考,自我意识了。 思考能力。。。。

    基本都是 copy别人的东西,自己也不太动脑子了,疯狂看别人的想法,实现,思想, 自己也不加联想和批判。 一股脑的 你说有什么优势 ,那我就 ‘嗯!’ 有什么优势,记也记不住什么东西。如同机械般的‘思考’ 和 ‘记忆’

     

  • 相关阅读:
    《程序员成长的烦恼》
    我们一起读《暗时间》
    CSS选择器分类总结
    CSharp如何自定义鼠标样式
    Android开发消除横向排列的多个Button之间的空隙
    JS代码指导原则
    Android蓝牙联机Demo解析
    排序算法之堆排序(Heapsort)解析
    排序算法之归并排序(Mergesort)解析
    经典串匹配算法(KMP)解析
  • 原文地址:https://www.cnblogs.com/cyy12/p/11588477.html
Copyright © 2020-2023  润新知