图已经是数据机构基础中最复杂的结构,它几乎涵盖了前面所有的基础数据结构,本篇涉及到了图的两种构造方式(邻接矩阵和邻接表),以及对应的深度优先遍历/广度优先遍历/Dijkstra/最小生成树/等方法,很多的方法代码复杂难以理解,共勉(●’◡’●)ノ♥
前言
我们将会遇到的应用使用几乎都是稀疏图——《算法第四版》
邻接矩阵的处理思路是将顶点和关系分别保存到一个一维数组和一个二维数组中。但是,即使我们保存的是int型数据,一旦数据量达到10万。那么这个数组需要使用的内存空间为:100000 * 100000 * 4Byte = 40GB 所以在这个时候邻接表的优势就很明显了.
邻接矩阵表示图
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
| package Graph;
public class Graph {
final int VERTEX_MAX = 10; Vertex[] vertex; int num; int [][] adjacency; class Vertex{ char content; boolean isSearch; public Vertex(char content) { this.content = content; this.isSearch = false; } } class Stack{ final int STACK_MAX = 10; int stack[]; int top; public Stack() { stack = new int[STACK_MAX]; top = -1; } public void push(int content) { if(top==9) { System.out.println("栈已满,无法入栈啦"); }else { stack[++top]=content; } } public int pop() { if(top==-1) { System.out.println("栈已空,无法出栈啦"); return -1; }else { return stack[top--]; } } public int peek() { return stack[top]; } public boolean isEmpty() { return top==-1; } } class Queue{ final int QUEUE_MAX = 10; int [] queue; int front; int rear; public Queue() { queue = new int[QUEUE_MAX]; front = 0; rear = -1; } public void insert(int content) { if(rear==QUEUE_MAX) { rear=-1; } queue[++rear]=content; } public int remove() { if(front==QUEUE_MAX) { front=0; } int temp = queue[front++]; return temp; } public boolean isEmpty() { return (rear+1==front || front+QUEUE_MAX-1==rear); } } public Graph() { this.num = 0; this.vertex = new Vertex[VERTEX_MAX]; this.adjacency = new int[VERTEX_MAX][VERTEX_MAX]; for(int i=0;i<VERTEX_MAX;i++) { for(int j=0;j<VERTEX_MAX;j++) { adjacency[i][j] = 0; } } } public void addVertex(char content) { if(num<VERTEX_MAX-1) { vertex[num++] = new Vertex(content); }else { System.out.println("图已满最大结点,不可以再添加结点了喔~"); } } public void addNAdj(int start, int end) { if(start<VERTEX_MAX && end<VERTEX_MAX) { adjacency[start][end] = 1; adjacency[end][start] = 1; }else { System.out.println("连接的图结点位置不合法,无法连接~"); } } public void addYAdj(int start, int end) { if(start<VERTEX_MAX && end<VERTEX_MAX) { adjacency[start][end] = -1; }else { System.out.println("连接的图结点位置不合法,无法连接~"); } } public void printVertex(int index) { if(index>=0 && index<VERTEX_MAX) { System.out.print(this.vertex[index].content); }else { System.out.println("打印结点位置不合法,无法打印~"); } } public void printAdjacency() { for(int i=0;i<num;i++) { for(int j=0;j<num;j++) { if(j!=num-1) { System.out.print(adjacency[i][j]+" "); }else { System.out.println(adjacency[i][j]); } } } } public int searchUnsearchVertex(int index) { for(int i=0;i<num;i++) { if(adjacency[i][index]==1 && vertex[i].isSearch==false) { return i; } } return -1; } public void dpt() { Stack stack = new Stack(); vertex[0].isSearch = true; printVertex(0); stack.push(0); while(!stack.isEmpty()) { int index = searchUnsearchVertex(stack.peek()); if(index==-1) { stack.pop(); }else { vertex[index].isSearch = true; printVertex(index); stack.push(index); } } for(int i=0;i<num;i++) { vertex[i].isSearch=false; } } public void bpt() { Queue queue = new Queue(); vertex[0].isSearch = true; printVertex(0); queue.insert(0); while(!queue.isEmpty()) { int v1 = queue.remove(); int v2 = searchUnsearchVertex(v1); if(v2!=-1) { vertex[v2].isSearch = true; printVertex(v2); queue.insert(v2); } } for(int i=0;i<num;i++) { vertex[i].isSearch = false; } } public void mst() { Stack stack = new Stack(); vertex[0].isSearch = true; stack.push(0); while(!stack.isEmpty()) { int cur = stack.peek(); int index = searchUnsearchVertex(0); if(index == -1) { stack.pop(); }else { vertex[index].isSearch = true; stack.push(index); printVertex(cur); printVertex(index); } } for(int i=0;i<num;i++) { vertex[i].isSearch = false; } } public static void main(String[] args) { Graph graph = new Graph(); graph.addVertex('F'); graph.addVertex('O'); graph.addVertex('R'); graph.addVertex('E'); graph.addVertex('V'); graph.addVertex('E'); graph.addVertex('R'); graph.addNAdj(1, 3); graph.addNAdj(2, 5); graph.addNAdj(3, 6); graph.addNAdj(0, 4); graph.addNAdj(0, 1); graph.printAdjacency(); graph.printVertex(3); System.out.println("深度优先遍历:"); graph.dpt(); System.out.println(); System.out.println("广度优先遍历:"); graph.bpt(); System.out.println(); System.out.println("最小生成树:"); graph.mst(); } }
|
邻接表的对应实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
| package Graph;
import java.lang.reflect.Array; import java.util.LinkedList; import java.util.Queue; import java.util.Stack;
public class GraphAdj<E> {
VNode<E>[] vers; //用来存储图的结点 int max_size; //图的最大结点数量 int numofvers; //图的当前结点数量 //邻接表建立图的结点 class VNode<E>{ E data; //存储定点数据 LNode first; //结点的邻接表的第一个结点 boolean isvisited = false; } //邻接表建立图的邻接表的结点 class LNode{ int weight; //存储权值 int firstadj; //邻接表结点的序号 LNode nextvex; //下一个邻接结点 public LNode(int weight, int firstadj) { this.weight = weight; this.firstadj = firstadj; } } //邻接表建立图的初始化 public GraphAdj(int max_size) { this.max_size = max_size; vers = (VNode[])Array.newInstance(VNode.class, max_size); } //获取图结点的树木 public int getNumOfGraph() { return numofvers; } //向图中插入结点 public boolean insert(E e) { if(numofvers>=max_size) { return false; } VNode<E> ver = new VNode<E>(); ver.data = e; vers[numofvers++] = ver; return true; } //获取某结点的位置 public int getIndex(E e) { for(int i=0;i<numofvers;i++) { if(vers[i].data == e) { return i; } } return -1; } //获取指定位置的结点 public E gerData(int index) { if(index<0 || index>=numofvers) { return null; }else { return vers[index].data; } } //向图中插入边 public boolean insertEdg(int v1, int v2, int weight) { if(v1<0 || v2<0 || v1>=numofvers || v2>=numofvers) { //合法性判断 return false; } LNode vers1 = new LNode(v2, weight); LNode vers2 = new LNode(v1, weight); //当v1结点没有邻接结点时 if(vers[v1].first==null) { vers[v1].first=vers1; }else {//当v1结点有邻接结点时 vers1.nextvex = vers[v1].first; vers[v1].first = vers1; } //当v2结点没有邻接结点时 if(vers[v2].first==null) { vers[v2].first = vers2; }else {//当v2结点有邻接结点时 vers2.nextvex = vers[v2].first; vers[v2].first = vers2; } return true; } //删除图中的某条边 public boolean deleteEdg(int v1, int v2) { if(v1<0 || v2<0 || v1>=numofvers || v2>=numofvers) { return false; } LNode current = vers[v1].first; LNode pre = null; //判断v1到v2之间是否存在边 while(current!=null && current.firstadj!=v2) { pre = current; current = current.nextvex; } //v1到v2之间存在边则删除 if(current!=null) { pre.nextvex = current.nextvex; } //判断v2到v1之间是否存在边 current = vers[v2].first; while(current!=null && current.firstadj!=v1) { pre = current; current = current.nextvex; } //v2到v1之间存在边则删除 if(current!=null) { pre.nextvex = current.nextvex; } return true; } //获得指定的结点间的边 public boolean getEdg(int v1, int v2) { if(v1<0 || v2<0 || v1>=numofvers || v2>=numofvers) { return false; } LNode current = vers[v1].first; while(current!=null) { if(current.firstadj == v2) { System.out.println("结点v"+v1+"->"+"结点v"+v2+"边的权值为:"+current.weight); } } current = vers[v2].first; while(current!=null) { if(current.firstadj == v2) { System.out.println("结点v"+v2+"->"+"结点v"+v1+"边的权值为:"+current.weight); } } return true; } //深度优先遍历 Deep-first traversal public void DFT(int v) { if(v<0 || v>=numofvers) { return; } Stack<Integer> stack = new Stack<Integer>(); vers[v].isvisited = true; LNode current; stack.push(v); while(stack!=null) { v = stack.pop(); System.out.println(vers[v].data+" "); current = vers[v].first; while(current!=null) { if(vers[current.firstadj].isvisited==false) { stack.push(current.firstadj); vers[current.firstadj].isvisited = true; } current = current.nextvex; } } //遍历完成后恢复初始状态 for(int i=0;i<numofvers;i++) { vers[i].isvisited = false; } } //广度优先遍历 Breadth-first traversal public void BFT(int v) { if(v<0 || v>=numofvers) { return; } Queue<Integer> queue = new LinkedList<Integer>(); LNode current; vers[v].isvisited = true; queue.offer(v); while(queue!=null) { v = queue.poll(); System.out.println(vers[v].data+" "); current = vers[v].first; while(current!=null) { if(vers[current.firstadj].isvisited==false) { queue.offer(current.firstadj); vers[current.firstadj].isvisited = true; } current = current.nextvex; } } //遍历完成后恢复初始状态 for(int i=0;i<numofvers;i++) { vers[i].isvisited = false; } } //Dijkstra最短路径
public int[] Dijkstra(int v) { if(v<0 || v>numofvers) { return null; } LNode current; current = vers[v].first; int[] distance = new int[numofvers]; for(int i=0;i<numofvers;i++) { distance[i] = Integer.MAX_VALUE; //即∞ } while(current!=null) { distance[current.firstadj] = current.weight; current = current.nextvex; } distance[v] = 0; vers[v].isvisited = true; for(int i=0;i<numofvers;i++) { int min = Integer.MAX_VALUE; int index = -1; // 比较从源点到其余顶点的路径长度 for (int j = 0; j < numofvers; j++) { // 从源点到j顶点的最短路径还没有找到 if (vers[j].isvisited == false) { // 从源点到j顶点的路径长度最小 if (distance[j] < min) { index = j; min = distance[j]; } } } // 找到源点到索引为index顶点的最短路径长度 if (index != -1) vers[index].isvisited = true; // 更新当前最短路径及距离 for (int t = 0; t < numofvers; t++) if (vers[t].isvisited == false) { current = vers[t].first; while (current != null) { if (current.firstadj == index) if ((min + current.weight) < distance[t]) { distance[t] = min + current.weight; break; } current = current.nextvex; } } } return distance; } }
|