数据结构-图
来源:互联网 发布:淘宝客推广能赚钱吗 编辑:程序博客网 时间:2024/06/05 10:38
简介
在数据结构上,图和树是比较相像的。实际上,树可以说是图的一种。
图包含由边连接的顶点。
图可以表示许多真实世界的情况,如飞机航线、电子线路和工作调度等。
搜索
有两种常用方法:深度优先搜索(DFS)和广度优先搜索(BFS)。最终都会到达所有连通的顶点。
深度优先搜索用栈来实现;广度优先搜索通过队列来实现。
深度优先搜索
规则
栈的内容就是从起始顶点到各个顶点访问的整个过程。从起始顶点出发访问下一个顶点时,就把这个顶点入栈。回到起始顶点时出栈。
- 如果可能,访问一个邻接的未访问顶点,标记它,并把它放入栈中;
- 当不能执行规则1时,如果栈不空,则从栈中弹出一个顶点;
- 如果不能执行规则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,则搜索结束。
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 著
- 数据结构-图
- 数据结构---图
- 数据结构-图
- 数据结构:图
- 数据结构--图
- 数据结构----------------【图】 .
- 数据结构--图
- 【数据结构】图
- 数据结构--图
- 数据结构-图
- 数据结构 - 图
- 数据结构:图
- 数据结构-图
- 【数据结构】【图】
- 数据结构 图
- 数据结构 图
- 数据结构-图
- 数据结构 图
- Android 网络图片查看器
- UVA 10054 The Necklace(欧拉回路)
- css中怎么让div里面的文字底部对齐
- NOIP模拟题 [DP][二分][树剖][树上差分]
- Android 网络请求
- 数据结构-图
- tomcat和nginx的区别
- poj 3686 The Windy's
- 个人记录-LeetCode 26. Remove Duplicates from Sorted Array
- Java基础 - 二叉树的三叉链表存储
- 黑色的那个星期一
- 20161109 - Day1
- Java基础 - 二叉树的二叉链表存储
- 微服务:dubbo与zookeeper的快速搭建