图
概念
- 顶点 vertex
- 边 edge
- 路径
- 无向图:顶点之间的连接没有方向
- 有向图:顶点之间的连接有方向
- 带权图:边带有权值,也叫网
图的表示方法
二维数组表示(邻接矩阵);链表表示(邻接表)
邻接矩阵:表示图形中顶点之间相邻关系的矩阵,对于n个顶点的图而言,矩阵是row和col表示的1…n个点
邻接表:
- 邻接矩阵需要为每个顶点都分配n个边的空间,其实有很多边都是不存在的,会造成空间的一定损失
- 邻接表的实现只关心存在的边,不关系不存在的边,因此没有空间浪费,邻接表由数组+链表组成
代码实现图结构
- 存储顶点 String ArrayList
- 保存矩阵:二维数组 int/[]/[] edges
package graph;
import java.util.ArrayList;
import java.util.Arrays;
public class GraphDemo01 {
private ArrayList<String> vertexList; // store vertex
private int[][] edges; // preliminary matrix corresponding to graph
private int numOfEdges; // # of edges
public static void main(String[] args) {
int n = 5;
String VertexValue[] = {"A","B","C","D","E"};
// create graph object
GraphDemo01 graph = new GraphDemo01(n);
// add vertex
for(String value:VertexValue){
graph.insertVertex(value);
}
// add edges
graph.insertEdge(0,1,1);
graph.insertEdge(0,2,1);
graph.insertEdge(1,2,1);
graph.insertEdge(1,3,1);
graph.insertEdge(1,4,1);
// show
graph.showGrapgh();
}
// constructor
public GraphDemo01(int n){
// initialize vertex and vertexList
edges = new int[n][n];
vertexList = new ArrayList<String>(n);
numOfEdges = 0;
}
// commonly used methods in graph
// return nums of vertex
public int getNumOfVertex(){
return vertexList.size();
}
// get nums of edges
public int getNumOfEdges(){
return numOfEdges;
}
// return info of vertex i, eg: 0-"A" 1-"B" 2-"C"
public String getValueByIndex(int i){
return vertexList.get(i);
}
// return weight between v1 and v2
public int getWeight(int v1,int v2){
return edges[v1][v2];
}
// show the matrix corresponding the graph
public void showGrapgh(){
for(int[] link:edges){
System.out.println(Arrays.toString(link));
}
}
// insert vertex
public void insertVertex(String vertex){
vertexList.add(vertex);
}
// add edges
/**
* add edges
* @param v1 index of the first vertex
* @param v2 index of the second vertex
* @param weight weight between v1 and v2
*/
public void insertEdge(int v1, int v2, int weight){
edges[v1][v2] = weight;
edges[v2][v1] = weight;
numOfEdges++;
}
}
图遍历
图遍历,即对节点的访问
策略:深度优先遍历;广度优先遍历
深度优先遍历
Depth First Search
- 从初始访问节点出发,初始访问节点可能有多个邻接节点,深度优先遍历的策略就是首先访问第一个邻接节点,然后再以这个被访问的邻接节点作为初始节点,访问它的第一个邻接节点。可以理解为:每次都在访问完当前节点后首先访问当前节点的第一个邻接节点
- 优先往纵向挖掘深入,而不是对一个节点所有邻接节点进行横向访问
- 深度优先搜索是一个递归的过程
算法步骤:
- 访问初始节点 v,并标记节点 v 为已访问
- 查找节点 v 的第一个邻接节点 w
- 若 w 存在,则继续执行 4,若 w不存在,则回到第1步,将从 v 的下一个节点继续
- 若 w 未被访问,对 w 进行深度优先遍历递归(即把 w 当作另一个 v,然后进行步骤 1,2,3)
- 查找节点 v 的 w 邻接节点的下一个邻接节点,转到步骤 3
代码
// constructor
public GraphDemo01(int n){
// initialize vertex and vertexList
edges = new int[n][n];
vertexList = new ArrayList<String>(n);
numOfEdges = 0;
isVisited = new boolean[5];
}
/**
* get the index of the first preliminary node
* @param index
* @return if exists, return index, or return -1
*/
public int getFirstNeighbor(int index){
for(int j = 0; j<vertexList.size();j++){
if(edges[index][j]>0){
return j;
}
}
return -1;
}
// get next neighbor node according to index of previous neighbor node
public int getNextNeighbor(int v1,int v2){
for(int j = v2+1;j<vertexList.size();j++){
if(edges[v1][j]>0){
return j;
}
}
return -1;
}
// dfs
// At first: i is 0
private void dfs(boolean[] isVisited,int i){
// visit (output) this node
System.out.println(getValueByIndex(i)+" -> ");
// set status of this node to visited
isVisited[i] = true;
// the first neighbor of i
int w = getFirstNeighbor(i);
while(w!=-1){
if(!isVisited[w]){
dfs(isVisited,w);
}
// if w has been visited
w = getNextNeighbor(i,w);
}
}
// override dfs, traverse all node and do dfs
public void dfs(){
for (int i = 0; i < getNumOfVertex(); i++) {
if(!isVisited[i]){
dfs(isVisited,i);
}
}
}
// main function
// test dfs
System.out.println("dfs");
graph.dfs();
广度优先遍历
Broad First Search
类似于一个分层搜索的过程,需要使用一个队列以保存访问过的节点的顺序,以按这个顺序来访问这些节点的邻接节点
算法步骤:
- 访问初始节点 v 并标记节点 v 已经被访问
- 节点 v 入队列
- 当队列非空时,继续执行,否则算法结束
- 出队列,取得队列头节点 u
- 查找节点 u 的第一个邻接节点 w
- 若节点 u 的邻接节点 w 不存在,则转到步骤3;否则执行以下三个步骤
- 若节点 w 尚未被访问,则访问节点 w 并标记为已访问
- 节点 w 入队列
- 查找节点 u 的继 w 邻接节点后的下一个邻接节点 w,转到步骤 6
代码
// bfs for one node (for loop for all nodes)
private void bfs(boolean[] isVisited,int i){
int u; // index of queue head
int w; // index of preliminary node
// queue: record order of node access
LinkedList queue = new LinkedList();
// visit this node, output it
System.out.println(getValueByIndex(i)+" -> ");
// mark as "visited"
isVisited[i] = true;
// add this node into queue(first in first out)
queue.addLast(i);
while(!queue.isEmpty()){
// pick up the head of queue
u = (Integer) queue.removeFirst();
// get the index of the first preliminary node
w = getFirstNeighbor(u);
while(w!=-1){ // find it
// whether it is visited
if(!isVisited[w]){
System.out.println(getValueByIndex(w)+" -> ");
// mark it as visited
isVisited[w] = true;
// in queue
queue.addLast(w);
}
// visited already
// take u as the previous node,find next preliminary node of w
w = getNextNeighbor(u,w);
}
}
}
// traverse all node, do bfs
public void bfs(){
for (int i = 0; i < getNumOfVertex(); i++) {
if(!isVisited[i]){
bfs(isVisited,i);
}
}
}
// main methods
// test bfs
System.out.println("bfs");
graph.bfs();