程序员

数据结构——图

2018-08-17  本文已影响11人  我哈啊哈啊哈

目录

1、相关术语

2、图的表示

2.1、邻接矩阵

2.2、邻接表

3、图的遍历

3.1、深度优先搜索

3.2、广度优先搜索

3.3、二者的比较

4、拓扑排序

5、最短路径算法

5.1、无权图中的最短路径

5.2、有权图中的最短路径

5.3、Bellman-Ford算法

6、最小生成树

6.1、Prim算法

6.2、Kruskal算法

正文

1、相关术语

2、图的表示

2.1、邻接矩阵

    public class Graph {
        private bool[,] adjMatrix;

        private int vertexCount;

        public Graph(int vertexCount) {
            this.vertexCount = vertexCount;
            adjMatrix = new bool[vertexCount,vertexCount];
        }

        public void addEdge(int i,int j) {
            if (i >= 0 && i < vertexCount && j >= 0 && j < vertexCount) {
                adjMatrix[i, j] = true;
                adjMatrix[j, i] = true;
            }
        }

        public void removeEdge(int i,int j) {
            if (i >= 0 && i < vertexCount && j >= 0 && j < vertexCount) {
                adjMatrix[i, j] = false;
                adjMatrix[j, i] = false;
            }
        }

        public bool isEdge(int i,int j) {
            if (i >= 0 && i < vertexCount && j >= 0 && j < vertexCount) {
                return adjMatrix[i, j];
            }
            else {
                return false;
            }
        }
    }

2.2、邻接表

    public class GraphByLinkList {
        private ArrayList<int> vertices;

        private ListNode[] edges;

        private int vertexCount;

        public GraphByLinkList(int vertexCount) {
            this.vertexCount = vertexCount;
            vertices = new ArrayList<int>();
            edges = new ListNode[vertexCount];
            for(int i = 0; i < vertexCount; i++) {
                vertices.add(i);
                edges[i] = new ListNode();
            }
        }

        public void addEdge(int source,int destination) {
            int i = vertices.indexOf(source);
            int j = vertices.indexOf(destination);
            if (i != -1 || j != -1) {
                edges[i].insertAtBeginning(destination);
                edges[j].insertAtBeginning(source);
            }
        }
    }

3、图的遍历

3.1、深度优先搜索

    public class Vertex {
        public char label { get; set; }
        public bool visited { get; set; }

        public Vertex(char label) {
            this.label = label;
            visited = false;
        }
    }

    public class Graph {
        private const int maxVertices = 20;

        /// <summary>
        /// 访问表
        /// </summary>
        private Vertex[] vertexList;

        /// <summary>
        /// 邻接表
        /// </summary>
        private int[,] adjMatrix;

        /// <summary>
        /// 顶点数
        /// </summary>
        private int vertexCount;

        /// <summary>
        /// 访问路径
        /// </summary>
        private Stack<int> theStack;

        public Graph() {
            vertexList = new Vertex[maxVertices];
            adjMatrix = new int[maxVertices, maxVertices];
            vertexCount = 0;
            for(int y = 0; y < maxVertices; y++) {
                for(int x = 0; x < maxVertices; x++) {
                    adjMatrix[x, y] = 0;
                }
            }
            theStack = new Stack<int>();
        }

        public void addVertex(char label) {
            vertexList[vertexCount++] = new Vertex(label);
        }

        public void addEdge(int start,int end) {
            adjMatrix[start, end] = 1;
            adjMatrix[end, start] = 1;
        }

        public void displayVertex(int v) {
            Console.Out.WriteLine(vertexList[v].label);
        }

        /// <summary>
        /// 深度优先搜索算法
        /// </summary>
        public void dfs() {
            vertexList[0].visited = true;
            displayVertex(0);
            theStack.Push(0);
            while (theStack.Count > 0) {
                int v = getAdjUnvisitedVertex(theStack.Peek());
                if (v == -1) {
                    theStack.Pop();
                }
                else {
                    vertexList[v].visited = true;
                    displayVertex(v);
                    theStack.Push(v);
                }
            }
            for(int j = 0; j < vertexCount; j++) {
                vertexList[j].visited = false;
            }
        }

        /// <summary>
        /// 获取从v顶点开始路径中,未被访问的顶点
        /// </summary>
        /// <param name="v">起始点</param>
        /// <returns>未访问点</returns>
        public int getAdjUnvisitedVertex(int v) {
            for(int j = 0; j < vertexCount; j++) {
                if (adjMatrix[v, j] == 1 && vertexList[j].visited == false) {
                    return j;
                }
            }
            return -1;
        }
    }
图3-2 图3-3 图3-4 图3-5 图3-6 图3-7 图3-8

3.2、广度优先搜索

    public class Vertex {
        public char label { get; set; }
        public bool visited { get; set; }
        public Vertex(char label) {
            this.label = label;
            visited = false;
        }
    }

    public class Graph {
        private const int maxVertices = 20;

        /// <summary>
        /// 访问表
        /// </summary>
        private Vertex[] vertexList;

        /// <summary>
        /// 邻接表
        /// </summary>
        private int[,] adjMatrix;

        /// <summary>
        /// 顶点数
        /// </summary>
        private int vertexCount;

        /// <summary>
        /// 访问路径
        /// </summary>
        private Queue<int> theQueue;

        public Graph() {
            vertexList = new Vertex[maxVertices];
            adjMatrix = new int[maxVertices, maxVertices];
            vertexCount = 0;
            for(int y = 0; y < maxVertices; y++) {
                for(int x = 0; x < maxVertices; x++) {
                    adjMatrix[x,y] = 0;
                }
            }
            theQueue = new Queue<int>();
        }

        public void addVertex(char label) {
            vertexList[vertexCount++] = new Vertex(label);
        }

        public void addEdge(int start, int end) {
            adjMatrix[start, end] = 1;
            adjMatrix[end, start] = 1;
        }

        public void displayVertex(int v) {
            Console.Out.WriteLine(vertexList[v].label);
        }

        /// <summary>
        /// 广度优先搜索算法
        /// </summary>
        public void bfs() {
            vertexList[0].visited = true;
            displayVertex(0);
            theQueue.Enqueue(0);
            int v2;
            while (theQueue.Count > 0) {
                int v1 = theQueue.Dequeue();
                while ((v2 = getAdjUnvisitedVertex(v1)) != -1) {
                    vertexList[v2].visited = true;
                    displayVertex(v2);
                    theQueue.Enqueue(v2);
                }
            }
            for (int j = 0; j < vertexCount; j++) {
                vertexList[j].visited = false;
            }
        }

        /// <summary>
        /// 获取从v顶点开始路径中,未被访问的顶点
        /// </summary>
        /// <param name="v">起始点</param>
        /// <returns>未访问点</returns>
        public int getAdjUnvisitedVertex(int v) {
            for (int j = 0; j < vertexCount; j++) {
                if (adjMatrix[v, j] == 1 && vertexList[j].visited == false) {
                    return j;
                }
            }
            return -1;
        }
    }
图3-10 图3-11

3.3、二者的比较

应用 深度优先搜索 广度优先搜索
生成森林、连通分量、路径、环路
最短路径
内存开销最小

4、拓扑排序

        public void TopologicalSort(Graph G) {
            LLQueue Q = new LLQueue();
            int counter;
            int v;
            counter = 0;
            //初始入队所有入度为0的顶点
            for (v = 0; v < G.vertexCount; v++) {
                if (G.indegree[v] == 0) {
                    Q.enQueue(v);
                }
            }
            while (!Q.isEmpty()) {
                v = Q.deQueue();
                topologicalOrder[v] = ++counter;
                //获取与v相邻的所有顶点
                var list = GetAdjacentTo(v);
                foreach(int w in list) {
                    if (--G.indegree[w] == 0) {
                        Q.enQueue(w);
                    }
                }
            }
            if (counter != G.vertexCount) {
                Console.Out.Write("Graph has cycle");
            }
            Q.deleteQueue();
        }

5、最短路径算法

5.1、无权图中的最短路径

顶点 Distance[v] 获得Distance[v]的前一个顶点
A -1 ——
B -1 ——
C 0 ——
D -1 ——
E -1 ——
F -1 ——
G -1 ——
        public void UnWeightedShortestPath(Graph G, int s) {
            LLQueue Q = new LLQueue();
            int v;
            Q.enQueue(s);
            for (int i = 0; i < G.vertexCount; i++) {
                Distance[i] = -1;
            }
            Distance[s] = 0;
            while (!Q.isEmpty()) {
                v = Q.deQueue();
                //获取与顶点v相邻的顶点集合
                var list = GetAdjacentTo(v);
                foreach (int w in list) {
                    //每个顶点最多检查一次
                    if (Distance[w] == -1) {
                        Distance[w] = Distance[v] + 1;
                        //存放最短路径中的上一个顶点
                        Path[w] = v;
                        //每个顶点最多入队一次
                        Q.enQueue(w);
                    }
                }
            }
            Q.deleteQueue();
        }

5.2、有权图中的最短路径(Dijkstra算法)

顶点 Distance[v] 获得Distance[v]的前一个顶点
A 0 ——
B -1 ——
C -1 ——
D -1 ——
E -1 ——
F -1 ——

1)、完成初始化后,从顶点A能够到达B和C,因此在距离表中以相应的边权值来更新顶点B和C的可达性,如图5-3所示。

图5-3 第一步
2)、从距离表中选择一个最小距离,可知最小距离是顶点C。这表明必须通过这两个顶点(A和C)才能到达其它顶点。而AC都能到达顶点B,这种情况下要选择代价小的路径,因为C到B的代价(1+2)更小,所以距离表中用3和顶点C来更新。通过C还可以到达顶点D,因此也相应的更新距离表中顶点D的值。如图5-4所示。
图5-4 第二步
3)、当前唯一未被访问的结点为E,为了到达 E,需要找出所有可以到达E的路径并选择其中代价最小的路径,可以发现,当使用经过C到达的B顶点作为中间顶点时具有最小代价。如图5-5所示。
图5-5 第三步
4)、最终产生的最小代价树如图5-6所示。
图5-6 最小代价树
        public void Dijkstra(Graph G,int s) {
            Heap PQ = new Heap();
            int v;
            PQ.enQueue(s);
            for(int i = 0; i < G.vertexCount; i++) {
                Distance[i] = -1;
            }
            Distance[s] = 0;
            while (!PQ.isEmpty()) {
                v = PQ.deleteMin();
                //获取与顶点v相邻的顶点集合
                var list = GetAdjacentTo(v);
                foreach (int w in list) {
                    int d = Distance[v] + weight[v, w];
                    //判断顶点w是否被访问过
                    if (Distance[w] == -1) {
                        //更新顶点w到源点的值
                        Distance[w] = d;
                        //加入优先队列
                        PQ.enQueue(w);
                        //更新顶点w的最短路径的上一顶点
                        Path[w] = v;
                    }
                    //判断当前路径是否最短
                    if (Distance[w] > d) {
                        Distance[w] = d;
                        //更新顶点w的最短路径的上一顶点
                        Path[w] = v;
                    }
                }
            }
        }

5.3、Bellman-Ford算法

        public void BellmanFordAlgorihm(Graph G,int s) {
            LLQueue Q = new LLQueue();
            int v;
            Q.enQueue(s);
            //假定用 INT_MAX填充距离表
            Distance[s] = 0;
            while (!Q.isEmpty()) {
                v = Q.deQueue();
                //获取与顶点v相邻的顶点集合
                var list = GetAdjacentTo(v);
                foreach (int w in list) {
                    int d = Distance[v] + weight[v, w];
                    if (Distance[w] > d) {
                        Distance[w] = d;
                        Path[w] = v;
                        if (!Q.isExist(w)) {
                            Q.enQueue(w);
                        }
                    }
                }
            }
        }

6、最小生成树

6.1、Prim算法

        public void Prims(Graph G,int s) {
            Heap PQ = new Heap();
            int v;
            PQ.enQueue(s);
            //假设距离表用 -1 填充
            Distance[s] = 0;
            while (!PQ.isEmpty()) {
                v = PQ.deleteMin();
                //获取与顶点v相邻的顶点集合
                var list = GetAdjacentTo(v);
                foreach (int w in list) {
                    int d = Distance[v] + weight[v, w];
                    if (Distance[w] == -1) {
                        //更新顶点w到源点的值
                        Distance[w] = d;
                        //加入优先队列
                        PQ.enQueue(w);
                        //更新顶点w的最短路径的上一顶点
                        Path[w] = v;
                    }
                    //判断当前路径是否最短
                    if (Distance[w] > d) {
                        Distance[w] = weight[v,w];
                        //更新顶点w的最短路径的上一顶点
                        Path[w] = v;
                    }
                }
            }
        }

6.2、Kruskal算法

上一篇 下一篇

猜你喜欢

热点阅读