图
在前面我们所介绍的树的数据结构中,我们可以明显的感觉到,树的表示是分层的,例如父子关系(1对多),而其他关系只能间接的表示,例如同级关系(1对1)。而图却不受这种限制。图是由顶点(或结点)及顶点之间的关系组成的集合。通常,图中的顶点数量或者一个顶点与其他顶点之间的连线的个数不受限制。表示多对多的关系。
图的表示方式:
连接矩阵表示法
如果0 和 1 联通,则 (0 ,1) = 1 。 如果0 和 4 联通 则 (0 ,4)= 1。
代码实现:
思路分析:
用集合{A , B , C , D , E}存储节点,集合的下标索引为二维数组的x y坐标值,如二维数组的0代表A。
public void insertEdges(int v1,int v2,int weight ){
edges[v1][v2] = weight;
edges[v2][v1] = weight;
num ++;
}
public void show(){
for (int[] link : edges){
System.out.println(Arrays.toString(link));
}
}
邻接表表示法
在邻接表表示法中,第一列代表的为结点,如0,1,2……,而后面的则代表为结点与其他结点相连接的结点。(例如0结点后面为1,4结点,则代表0结点与1结点和4结点相连接【在这里我们可以发现,第5行的4结点的后面同样有1结点】)
深度优先算法(DFS)
- 深度优先遍历,从初始访问节点出发,初始访问节点可能有多个邻接点,深度优先遍历的策略就是首先访问第一个邻接点,然后再以这个被访问的邻接节点作为初始节点,访问它的第一个邻接节点。
- 深度优先遍历的访问策略就是优先往纵向挖掘深入,而不是对一个节点的所有邻接节点进行横向访问。
- 深度优先算法是一个递归的过程。
算法步骤:
- 访问初始节点v,并标记v已被访问。
- 查找节点v的第一个邻接节点w。
- 若w存在,则继续执行4,如果不存在,则回到第一步,将从v的下一个节点继续。
- 若w未被访问,对w进行深度优先遍历递归(把w当作另一个v,进行步骤123)
- 查找节点v的下一个邻接点,转到步骤3。
代码实现:
代码实现:
//深度优先遍历算法
public void dfs(boolean[] isVisited , int i){
//首先访问该节点,输出其对应的顶点名称(用数组下标代替的 如0代表A)
System.out.println(vertexs.get(i));
//设置节点已经被访问过
isVisited[i] = true;
//查找节点i的第一个邻接点w,其实代表的是列。
int w = getFirstNeighbor(i);
//当w 存在时
while (w != -1){
//如果没被访问过
if (!isVisited[w])
dfs(isVisited,w);
//如果已被访问,则找i的下一个邻接点
w = getNextNeighbor(i,w);
}
}
//对dfs 进行一个重载,遍历我们所有的节点,并进行dfs,完成一个节点遍历完后,指向下一个节点。(回溯)
public void dfs(){
isVisited = new boolean[vertexs.size()];
//遍历所有的节点,进行dfs回溯
for(int i = 0 ; i < getNumOfEdges() ; i++){
if (!isVisited[i]){
dfs(isVisited,i);
}
}
}
关键方法:
//得到某节点,第一个邻接邻接点的下标 w
public int getFirstNeighbor(int index){
//从第index排 从左向右看有无非零0(邻接点)
for (int j = 0;j < vertexs.size(); j++){
if(edges[index][j] > 0)
return j;
}
return -1;
}
//根据前一个邻接点的下标来获取下一个邻接点。
//v1 为 前一个邻接点所在行数, v2 为前一个邻接点所在列数
public int getNextNeighbor(int v1,int v2){
for (int j = v2 + 1; j < vertexs.size(); j++){
if(edges[v1][j] > 0)
return j;
}
return -1;
}
广度优先算法
广度优先算法类似于一个分层搜索的过程,广度优先遍历需要使用一个队列以保持访问过的节点的顺序,以便按照这个顺序来访问这些节点的邻接节点。
算法步骤:
- 访问初始节点 v 并标记节点 v 已被访问。
- 节点v 入队列。
- 当队列非空时,继续执行,否则算法结束。
- 出队列,取得队列头节点u。
- 查找节点u的第一个邻接节点w。
- 若节点u的邻接节点w不存在,则回到步骤3。否则循环执行以下三个步骤:
- 若节点w尚未被访问,则访问节点w ,并标记已访问。
- 节点w入队列。
- 查找节点u的继w邻接节点后的下一个邻接节点w,转到步骤6。
代码实现:
//bfs 广度优先算法
public void bfs(boolean[] isVisited , int i){
//记录邻接点索引
int w;
//记录队列头对应的图节点索引
int u;
System.out.println(vertexs.get(i));
LinkedList queue = new LinkedList();
//入队
queue.add(i);
//队列非空,算法继续
while (!queue.isEmpty()){
//出队,获得头队列对应的图节点索引,进行遍历
u = (Integer) queue.removeFirst();
// 查找节点 u 的邻接点w
w = getFirstNeighbor(u);
// 判断w 是否存在
while (w != -1){
if(isVisited[w]){
// 访问节点w
System.out.println(w);
// 标记已访问
isVisited[w] = true;
// 加入到队列里
queue.add(w);
}
//以 u 为 前驱点 找 w 后面的下一个邻接点
w = getNextNeighbor(u , w) ;
}
}
}
// 重载bfs
public void bfs(){
isVisited = new boolean[vertexs.size()];
for (int i = 0; i < isVisited.length; i++) {
if (!isVisited[i])
bfs(isVisited,i);
}
}