无向图-笔记-代码

//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 += "\n";
        }
        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("\n=== 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别人的东西,自己也不太动脑子了,疯狂看别人的想法,实现,思想, 自己也不加联想和批判。 一股脑的 你说有什么优势 ,那我就 ‘嗯!’ 有什么优势,记也记不住什么东西。如同机械般的‘思考’ 和 ‘记忆’

 

posted on 2019-09-26 00:03  jald  阅读(317)  评论(0编辑  收藏  举报

导航