数据结构-图

来源:互联网 发布:淘宝客推广能赚钱吗 编辑:程序博客网 时间:2024/06/05 10:38

简介

在数据结构上,图和树是比较相像的。实际上,树可以说是图的一种。

图包含由边连接的顶点。

图可以表示许多真实世界的情况,如飞机航线、电子线路和工作调度等。

搜索

有两种常用方法:深度优先搜索(DFS)和广度优先搜索(BFS)。最终都会到达所有连通的顶点。

深度优先搜索用栈来实现;广度优先搜索通过队列来实现。

深度优先搜索


规则

  1. 如果可能,访问一个邻接的未访问顶点,标记它,并把它放入栈中;
  2. 当不能执行规则1时,如果栈不空,则从栈中弹出一个顶点;
  3. 如果不能执行规则1和规则2,则整个搜索过程结束。
栈的内容就是从起始顶点到各个顶点访问的整个过程。从起始顶点出发访问下一个顶点时,就把这个顶点入栈。回到起始顶点时出栈。

深度优先搜索算法要得到距离起始顶点最远的顶点,然后在不能继续前进的时候返回。

Java实现

package com.jikefriend.graph;public class GraphDfs {    static class StackX {        private final int SIZE = 20;        private int[] st;        private int top;        public StackX() {            st = new int[SIZE];            top = -1;        }        public void push(int j) {            st[++top] = j;        }        public int pop() {            return st[top--];        }        public int peek() {            return st[top];        }        public boolean isEmpty() {            return top == -1;        }    }    static class Vertex {        public char label;        public boolean wasVisited;        public Vertex(char lab) {            label = lab;            wasVisited = false;        }    }    static class Graph {        private final int MAX_VERTS = 20;        private Vertex[] vertexList;        private int[][] adjMat;        private int nVerts;        private StackX theStack;        public Graph() {            vertexList = new Vertex[MAX_VERTS];            adjMat = new int[MAX_VERTS][MAX_VERTS];            nVerts = 0;            for (int i = 0; i < MAX_VERTS; i++) {                for (int j = 0; j < MAX_VERTS; j++) {                    adjMat[i][j] = 0;                }            }            theStack = new StackX();        }        public void addVertex(char lab) {            vertexList[nVerts++] = new Vertex(lab);        }        public void addEdge(int start, int end) {            adjMat[start][end] = 1;            adjMat[end][start] = 1;        }        public void displayVertex(int v) {            System.out.print(vertexList[v].label);        }        /**         * 深度优先搜索,步骤:         *  1、用peek()方法检查栈顶的顶点;         *  2、试图找到这个顶点还未访问的邻接点;         *  3、如果没有找到,出栈;         *  4、如果找到这样的顶点,访问这个顶点,并把它放入栈。         */        public void dfs() {            vertexList[0].wasVisited = true;  //标记为已访问            displayVertex(0);            theStack.push(0);                 //放入栈            while (!theStack.isEmpty()) {                /** 试图找到这个顶点还未访问的邻接点 */                int v = getAdjUnvisitedVertex(theStack.peek());                if (v == -1)                    /** 如果没有找到,出栈 */                    theStack.pop();                else {                    /** 如果找到这样的顶点,访问这个顶点,并把它放入栈 */                    vertexList[v].wasVisited = true;  //标记为已访问                    displayVertex(v);                    theStack.push(v);                 //放入栈                }            }            /** 重置所有访问标记 */            for (int i = 0; i < nVerts; i++) {                vertexList[i].wasVisited = false;            }        }        /**         * 找出与顶点邻接且没有访问过的顶点         *         * @param v         * @return         */        public int getAdjUnvisitedVertex(int v) {            for (int i = 0; i < nVerts; i++) {                if (adjMat[v][i] == 1 && vertexList[i].wasVisited == false)                    return i;            }            return -1;        }    }    public static void main(String[] args) {        Graph graph = new Graph();        graph.addVertex('A');        graph.addVertex('B');        graph.addVertex('C');        graph.addVertex('D');        graph.addVertex('E');        graph.addEdge(0, 1);        graph.addEdge(1, 2);        graph.addEdge(0, 3);        graph.addEdge(3, 4);        System.out.print("Visits: ");        graph.dfs();        System.out.println();    }}

决策树

首次移动的是根节点,之后有N种可能的移动,那么有N个第二层节点与根节点相连,接下来有N-1中可能的移动,所以每个第二层节点就有N-1个第三层节点与之相连。从根节点到叶节点,为构造这颗树需要有N!条路径。这就是决策树。

广度优先搜索


深度优先搜索是尽快的远离起始顶点。而广度优先搜索则恰恰与之相反,要尽可能的靠近起始顶点。首先访问的是起始顶点的所有邻接点,然后再访问较远的区域。广度优先搜索就像水中的波纹层层扩展。

规则

  1. 访问下一个未访问的邻接点(如果存在),这个顶点必须是当前顶点的邻接点,标记它,并把它插入到队列中;
  2. 如果因为已经没有未访问顶点而不能执行规则1,那么从队列头取一个顶点(如果存在),并使其成为当前顶点;
  3. 如果因为队列为空而不能执行规则2,则搜索结束。

Java实现

package com.jikefriend.graph;public class GraphBfs {    static class Queue {        private final int SIZE = 20;        private int[] queArray;        private int front;        private int rear;        public Queue() {            queArray = new int[SIZE];            front = 0;            rear = -1;        }        public void insert(int j) {            if (rear == SIZE - 1)                rear = -1;            queArray[++rear] = j;        }        public int remove() {            int temp = queArray[front++];            if (front == SIZE)                front = 0;            return temp;        }        public boolean isEmpty() {            return (rear + 1 == front) || (front + SIZE - 1 == rear);        }    }    static class Vertex {        public char label;        public boolean wasVisited;        public Vertex(char lab) {            label = lab;            wasVisited = false;        }    }    static class Graph {        private final int MAX_VERTS = 20;        private Vertex[] vertexList;        private int[][] adjMat;        private int nVerts;        private Queue theQueue;        public Graph() {            vertexList = new Vertex[MAX_VERTS];            adjMat = new int[MAX_VERTS][MAX_VERTS];            nVerts = 0;            for (int i = 0; i < MAX_VERTS; i++) {                for (int j = 0; j < MAX_VERTS; j++) {                    adjMat[i][j] = 0;                }            }            theQueue = new Queue();        }        public void addVertex(char lab) {            vertexList[nVerts++] = new Vertex(lab);        }        public void addEdge(int start, int end) {            adjMat[start][end] = 1;            adjMat[end][start] = 1;        }        public void displayVertex(int v) {            System.out.print(vertexList[v].label);        }        public void bfs() {            vertexList[0].wasVisited = true;            displayVertex(0);            theQueue.insert(0);            int v2;            while (!theQueue.isEmpty()) {                int v = theQueue.remove();                while ((v2 = getAdjUnvisitedVertex(v)) != -1) {                    vertexList[v2].wasVisited = true;                    displayVertex(v2);                    theQueue.insert(v2);                }            }            for (int i = 0; i < nVerts; i++) {                vertexList[i].wasVisited = false;            }        }        public int getAdjUnvisitedVertex(int v) {            for (int i = 0; i < nVerts; i++) {                if (adjMat[v][i] == 1 && vertexList[i].wasVisited == false)                    return i;            }            return -1;        }    }    public static void main(String[] args) {        Graph graph = new Graph();        graph.addVertex('A');        graph.addVertex('B');        graph.addVertex('C');        graph.addVertex('D');        graph.addVertex('E');        graph.addEdge(0, 1);        graph.addEdge(1, 2);        graph.addEdge(0, 3);        graph.addEdge(3, 4);        System.out.print("Visits: ");        graph.bfs();        System.out.println();    }}

最小生成树


只通过最少量的边来保证顶点彼此连通,这就组成了最小生成树。同一组顶点,可能有多种最小生成树。

最小生成树边E的数量总比顶点V的数量小1:

E = V - 1

使用深度优先搜索的Java实现

package com.jikefriend.graph;public class GraphMst {    static class StackX {        private final int SIZE = 20;        private int[] st;        private int top;        public StackX() {            st = new int[SIZE];            top = -1;        }        public void push(int j) {            st[++top] = j;        }        public int pop() {            return st[top--];        }        public int peek() {            return st[top];        }        public boolean isEmpty() {            return top == -1;        }    }    static class Vertex {        public char label;        public boolean wasVisited;        public Vertex(char lab) {            label = lab;            wasVisited = false;        }    }    static class Graph {        private final int MAX_VERTS = 20;        private Vertex[] vertexList;        private int[][] adjMat;        private int nVerts;        private StackX theStack;        public Graph() {            vertexList = new Vertex[MAX_VERTS];            adjMat = new int[MAX_VERTS][MAX_VERTS];            nVerts = 0;            for (int i = 0; i < MAX_VERTS; i++) {                for (int j = 0; j < MAX_VERTS; j++) {                    adjMat[i][j] = 0;                }            }            theStack = new StackX();        }        public void addVertex(char lab) {            vertexList[nVerts++] = new Vertex(lab);        }        public void addEdge(int start, int end) {            adjMat[start][end] = 1;            adjMat[end][start] = 1;        }        public void displayVertex(int v) {            System.out.print(vertexList[v].label);        }        public void mst() {            vertexList[0].wasVisited = true;            theStack.push(0);            while (!theStack.isEmpty()) {                int currentVertex = theStack.peek();                int v = getAdjUnvisitedVertex(currentVertex);                if (v == -1)                    theStack.pop();                else {                    vertexList[v].wasVisited = true;                    theStack.push(v);                    displayVertex(currentVertex);                    displayVertex(v);                    System.out.print(" ");                }            }            for (int i = 0; i < nVerts; i++) {                vertexList[i].wasVisited = false;            }        }        public int getAdjUnvisitedVertex(int v) {            for (int i = 0; i < nVerts; i++) {                if (adjMat[v][i] == 1 && vertexList[i].wasVisited == false)                    return i;            }            return -1;        }    }    public static void main(String[] args) {        Graph graph = new Graph();        graph.addVertex('A');        graph.addVertex('B');        graph.addVertex('C');        graph.addVertex('D');        graph.addVertex('E');        graph.addEdge(0, 1);        graph.addEdge(0, 2);        graph.addEdge(0, 3);        graph.addEdge(0, 4);        graph.addEdge(1, 2);        graph.addEdge(1, 3);        graph.addEdge(1, 4);        graph.addEdge(2, 3);        graph.addEdge(2, 4);        graph.addEdge(3, 4);        System.out.print("Minimum spanning tree: ");        graph.mst();        System.out.println();    }}

有向图

边有方向的图,称为有向图,如下图所示。


无向图的邻接矩阵是上下三角对称的,而有向图的邻接矩阵是非对称的,用以区分边的方向。

拓扑排序,即某些项目或事件必须按特定的顺序排列或发生,如下图。

拓扑排序算法步骤:

找到一个没有后继的顶点;

从图中删除这个顶点,在列表的前面插入顶点的标记。

重复步骤1和步骤2,知道所有的顶点都从图中删除。此时列表显示的顶点顺序就是拓扑排序的结果。

如果顶点没有后继,那么它肯定是拓扑序列中的最后一个,一旦删除它,剩下的顶点中必然有一个没有后继,所以它成为下一个拓扑序列中的最后一个,以此类推。

如果有N个顶点的图有超过N-1条边,那么它必定存在环。路径的起点和终点都是同一个顶点,则成为环。

拓扑排序必须在无环的有向图中进行。

拓扑排序Java实现

package com.jikefriend.graph;public class GraphTopo {    static class Vertex {        public char label;        public Vertex(char lab) {            label = lab;        }    }    static class Graph {        private final int MAX_VERTS = 20;        private Vertex[] vertexList;        private int[][] adjMat;        private int nVerts;        private char[] sortedArray;        public Graph() {            vertexList = new Vertex[MAX_VERTS];            adjMat = new int[MAX_VERTS][MAX_VERTS];            nVerts = 0;            for (int i = 0; i < MAX_VERTS; i++) {                for (int j = 0; j < MAX_VERTS; j++) {                    adjMat[i][j] = 0;                }            }            sortedArray = new char[MAX_VERTS];        }        public void addVertex(char lab) {            vertexList[nVerts++] = new Vertex(lab);        }        public void addEdge(int start, int end) {            adjMat[start][end] = 1;        }        public void displayVertex(int v) {            System.out.print(vertexList[v].label);        }        /**         * 拓扑排序         */        public void topo() {            int orig_nVerts = nVerts;            while (nVerts > 0) {                /** 找到任意一个没有后继的顶点 */                int currentVertex = noSuccessors();                /** 如果没有这样的顶点,则图必然存在环 */                if (currentVertex == -1) {                    System.out.println("Error: Graph has cycles");                    return;                }                /** 如果找到这样的顶点,放入数组,并删除它 */                sortedArray[nVerts - 1] = vertexList[currentVertex].label;                deleteVertex(currentVertex);            }            System.out.print("Topologically sorted order: ");            for (int i = 0; i < orig_nVerts; i++)                System.out.print(sortedArray[i]);            System.out.println();        }        /**         * 找到一个没有后继的顶点         *          * @return         */        public int noSuccessors() {            boolean isEdge;            for (int row = 0; row < nVerts; row++) {                isEdge = false;                for (int col = 0; col < nVerts; col++) {                    if (adjMat[row][col] > 0) {                        isEdge = true;                        break;                    }                }                if (!isEdge)                    return row;            }            return -1;        }        /**         * 删除顶点,并调整邻接矩阵         *          * @param delVert         */        public void deleteVertex(int delVert) {            if (delVert != nVerts - 1) {                for (int i = delVert; i < nVerts - 1; i++)                    vertexList[i] = vertexList[i + 1];                for (int row = delVert; row < nVerts - 1; row++)                    moveRowUp(row, nVerts);                for (int col = delVert; col < nVerts - 1; col++)                    moveColLeft(col, nVerts - 1);            }            nVerts--;        }        /**         * 删除顶点后上移邻接矩阵         *          * @param row         * @param length         */        private void moveRowUp(int row, int length) {            for (int col = 0; col < length; col++) {                adjMat[row][col] = adjMat[row + 1][col];            }        }        /**         * 删除顶点后左移邻接矩阵         *          * @param col         * @param length         */        private void moveColLeft(int col, int length) {            for (int row = 0; row < length; row++) {                adjMat[row][col] = adjMat[row][col + 1];            }        }    }    public static void main(String[] args) {        Graph graph = new Graph();        graph.addVertex('A');        graph.addVertex('B');        graph.addVertex('C');        graph.addVertex('D');        graph.addVertex('E');        graph.addVertex('F');        graph.addVertex('G');        graph.addVertex('H');        graph.addEdge(0, 3);        graph.addEdge(0, 4);        graph.addEdge(1, 4);        graph.addEdge(2, 5);        graph.addEdge(3, 6);        graph.addEdge(4, 6);        graph.addEdge(5, 7);        graph.addEdge(6, 7);        graph.topo();    }}

摘自《Java数据结构算法(第二版)》 [美] Robert Lafore 著

0 0
原创粉丝点击