?
?
?
?
?
?
?
?
//深度优先遍历算法
//i 第一次就是0
private void dfs(boolean[] isVisited, int i) {
//1.首先,我们访问该结点,就是输出
System.out.print(getValueByIndex(i) + "->");
//访问完后将该结点设置成已经访问
isVisited[i] = true;
//2.查找结点v(i)的第一个邻接结点w
int w = getFirstNeighbor(i);
//3.若w存在,则执行4,如果w不存在,则回到第一步,将从v的下一个结点继续
while (w != -1) {//说明存在,然后得先判断是否被访问郭
//4.若w未被访问,则对w进行深度优先遍历递归(即把w当作另一个v,然后进行步骤1,2,3)
if (!isVisited[w]) {//
dfs(isVisited, w);
}
//如果已经被访问过
//5.查找结点v的w的邻接结点的下一个邻接结点,转到步骤3
w = getNextNeighbor(i, w);
}
}
//对dfs进行重载,对应w不存在的情况
public void dfs() {
isVisited = new boolean[5];
//遍历所有的节点,进行dfs[回溯]
for (int i = 0; i < getNumOfVertex(); i++) {
if (!isVisited[i]) {
dfs(isVisited, i);
}
}
}
?
?
//对一个结点进行广度优先遍历的方法
private void bfs(boolean[] isVisited,int i){
int u;//表示队列的头节点对应的下标
int w;//表示邻接结点的w,对应的下标
//队列,记录结点访问的顺序
LinkedList queue = new LinkedList();
//访问结点,输出结点信息
System.out.print(getValueByIndex(i)+"->");
//访问完后标记为已经访问
isVisited[i]=true;
//将结点加入队列
queue.addLast(i);//队列的特点:加入的时候从尾部加入,取的时候从头部开始取
while (! queue.isEmpty()){//只要队列非空,就一直进行
//取出队列头节点的下标
u=(Integer)queue.removeFirst();//自动拆箱
//得到第一个邻接点的下标w
w=getFirstNeighbor(u);
while (w!=-1){//说明找到
//判断是否访问过
if (!isVisited[w]){//未访问,直接输出
System.out.print(getValueByIndex(w)+"->");
//标记为已访问
isVisited[w]=true;
//入队
queue.addLast(w);
}
//如果已经访问过,那么还是以u为前驱找w后面的下一个邻接点。
w=getNextNeighbor(u,w);//这个地方就体现出广度优先
}//此时,在第一个层走到D不通了,这个while循环退出,再重新从外层大循环开始走,先判断队列是否为空,那么u变成1
}
}
//遍历所有的结点,都进行广度优先遍历搜索
public void bfs(){
isVisited = new boolean[5];
for (int i = 0; i < getNumOfVertex(); i++) {
if (!isVisited[i]){
bfs(isVisited,i);
}
}
}
?完整代码1:
package com.atguigu.graph;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
/**
* 图的创建与遍历
* 图的遍历一般有两种策略:1,深度优先遍历 2广度优先遍历
* 深度优先遍历:每次都在访问完当前结点后首先访问当前结点的第一个邻接结点
* 比如:v1-v2-v3,先访问v1,然后访问v2,然后以v2为当前结点(当作第一个结点)访问v3,如果v1还和v3相连也是先访问v2
* 优先向纵向挖掘深入,而不是对一个结点的所有邻接结点进行横向访问。
* 显然是一个递归的过程
* 算法步骤:
* 1.访问初始结点v,并标记点v已经访问。
* 2.查找结点v的第一个结点w
* 3.若w存在,则执行4,如果w不存在,则回到第一步,将从v的下一个结点继续(这里感觉应该是v的上一个结点进行访问)
* 4.若w未被访问,则对w进行深度优先遍历递归(即把w当作另一个v,然后进行步骤1,2,3)
* 5.查找结点v的w的邻接结点的下一个邻接结点,转到步骤3
* 如图具体路径应该是ABCBDBEBA
* B
* C D E
* A
*
* 二.广度优先的算法步骤;这个就相当于一层一层的遍历,就是先把第一层能访问的全部访问过来一遍
* 1.访问初始节点v并标记为v已经访问
* 2.结点v入队列
* 3.当队列为空时,继续执行,否则算法结束
* 4.出队列,取得队头节点u
* 5.查找结点u的第一个邻接点w
* 6.若结点u的邻接点w不存在,则转到步骤3;否则执行一下三个步骤
* 6.1. 若结点w尚未被访问,则访问结点w并标记为已访问
* 6.2. 结点w入队列
* 6.3. 查找结点u的即w邻接结点后的下一个邻接结点w,转到步骤6
*
*
* @author WZ
* @create 2021-11-17 20:45
*/
public class Graph {
public static void main(String[] args) {
//测试一把图是否创建正确
int n = 5;//结点的个数
String[] vertexs = new String[]{"A", "B", "C", "D", "E"};
//创建图的对象
Graph graph = new Graph(n);
//循环的添加结点
for (String vertex : vertexs) {
graph.vertexList.add(vertex);
}
//添加边的操作
//A-B A-C B-C B-D B-E 这几个相连A0,B1,C2,D3,E4
graph.insertEdge(0, 1, 1);//这个表示A-B已经相连接
graph.insertEdge(0, 2, 1);//A-C
graph.insertEdge(1, 2, 1);//B-C
graph.insertEdge(1, 3, 1);//B-D
graph.insertEdge(1, 4, 1);//B-E
//显示一把邻接矩阵
graph.showGraph();
//测试深度遍历
System.out.println("深度遍历");
graph.dfs();
System.out.println();
//测试广度优先遍历
System.out.println("广度优先遍历测试");
graph.bfs();
}
private ArrayList<String> vertexList;//存储顶点的集合
private int[][] edges;//存储图对应的邻结矩阵
private int numOfEdges;//表示边的数目
//定义一个数组boolean[],记录某个结点是否被访问
private boolean[] isVisited;
//深度优先算法的实现
//得到第一个临界结点的下标w;在矩阵中先找到某一行,再找到对应的列。。从左往右一直找
public int getFirstNeighbor(int index) {//index表示当前结点的下标,指的是行指的是要出发的结点,j是遍历的列,指的是要到的结点,如果存在返回对应的下标,
for (int j = 0; j < vertexList.size(); j++) {
if (edges[index][j] > 0) {
return j;
}
}
return -1;
}
//这个主要是这样的,比如先从,A开始,在A的那一行往右走,第一个是0,没有关系不输出,然后继续往后走
//数字为1,说明A和B有关系,B是A的邻接点,然后可以输出B,此时B已经被访问需要在数组设成已经访问,
//然后来到B所在的行!!!!!这个是深度遍历重点,然后往右走,第一个是1,说明A是邻接点,但是A已经
//被访问了,继续往右走,0,不行找到第三个数字为1,说明C是B的邻接点,然后记录C已经被访问。
//然后找到C所在的行,继续从左往右走,A,B虽然都是1但是已经被访问过,然后C没有邻接点了,然后退回到B
//B从左往右走,一直走到D是邻接点(前面已经访问过了)。。。依此类推。
/* A B C D E
A [0,1,1,0,0]
B [1,0,1,1,1]
C [1,1,0,0,0]
D [0,1,0,0,0]
E [0,1,0,0,0]
*/
//根据前一个邻接结点的下标来获取下一个邻接结点
/**
* @param v1 指的是V1行
* @param v2 指的是V2列,并且V2是V1的邻接结点,即edges[v1][v2]==1,这个方法是找v1的下一个邻接结点,所以j=v2+1
* 上面那个只能得到第一个邻接结点的下标。而这个和那个不同,尤其对于邻接结点已经被访问,不能直接用上面那个
* @return
*/
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;
}
//深度优先遍历算法
//i 第一次就是0
private void dfs(boolean[] isVisited, int i) {
//1.首先,我们访问该结点,就是输出
System.out.print(getValueByIndex(i) + "->");
//访问完后将该结点设置成已经访问
isVisited[i] = true;
//2.查找结点v(i)的第一个邻接结点w
int w = getFirstNeighbor(i);
//3.若w存在,则执行4,如果w不存在,则回到第一步,将从v的下一个结点继续
while (w != -1) {//说明存在,然后得先判断是否被访问郭
//4.若w未被访问,则对w进行深度优先遍历递归(即把w当作另一个v,然后进行步骤1,2,3)
if (!isVisited[w]) {//
dfs(isVisited, w);
}
//如果已经被访问过
//5.查找结点v的w的邻接结点的下一个邻接结点,转到步骤3
w = getNextNeighbor(i, w);
}
}
//对dfs进行重载,对应w不存在的情况
public void dfs() {
isVisited = new boolean[5];
//遍历所有的节点,进行dfs[回溯]
for (int i = 0; i < getNumOfVertex(); i++) {
if (!isVisited[i]) {
dfs(isVisited, i);
}
}
}
//对一个结点进行广度优先遍历的方法
private void bfs(boolean[] isVisited,int i){
int u;//表示队列的头节点对应的下标
int w;//表示邻接结点的w,对应的下标
//队列,记录结点访问的顺序
LinkedList queue = new LinkedList();
//访问结点,输出结点信息
System.out.print(getValueByIndex(i)+"->");
//访问完后标记为已经访问
isVisited[i]=true;
//将结点加入队列
queue.addLast(i);//队列的特点:加入的时候从尾部加入,取的时候从头部开始取
while (! queue.isEmpty()){//只要队列非空,就一直进行
//取出队列头节点的下标
u=(Integer)queue.removeFirst();//自动拆箱
//得到第一个邻接点的下标w
w=getFirstNeighbor(u);
while (w!=-1){//说明找到
//判断是否访问过
if (!isVisited[w]){//未访问,直接输出
System.out.print(getValueByIndex(w)+"->");
//标记为已访问
isVisited[w]=true;
//入队
queue.addLast(w);
}
//如果已经访问过,那么还是以u为前驱找w后面的下一个邻接点。
w=getNextNeighbor(u,w);//这个地方就体现出广度优先
}//此时,在第一个层走到D不通了,这个while循环退出,再重新从外层大循环开始走,先判断队列是否为空,那么u变成1
}
}
//遍历所有的结点,都进行广度优先遍历搜索
public void bfs(){
isVisited = new boolean[5];
for (int i = 0; i < getNumOfVertex(); i++) {
if (!isVisited[i]){
bfs(isVisited,i);
}
}
}
//基本准备
//构造器
public Graph(int n) {//n表示构建这个图有多少个顶点
//初始化矩阵和vertexList
edges = new int[n][n];
vertexList = new ArrayList<>(n);
numOfEdges = 0;//初始化为0
}
//插入节点
public void insertVertex(String vertex) {
vertexList.add(vertex);
}
//添加边
/**
* @param v1 表示点的下标,即,第几个顶点,比如:描述AB之前的关系,A对应的是0,B对应的为1,在矩阵中
* @param v2 表示第二个顶点的下标
* @param weight 这个权值表示这条边对应的权值,在矩阵中,要么是1,要么是0,0表示二者有无关联,1表示二者有关联
*/
public void insertEdge(int v1, int v2, int weight) {
edges[v1][v2] = weight;
edges[v2][v1] = weight;
numOfEdges++;
}
//图中常用的方法
//返回节点的个数
public int getNumOfVertex() {
return vertexList.size();
}
//得到边的数目
public int getNumOfEdges() {
return numOfEdges;
}
//返回结点i(下标)对应的数据:0返回的是A,1返回的是B,2返回的是C
public String getValueByIndex(int i) {
return vertexList.get(i);
}
//返回v1和v2的权值
public int insertVertex(int v1, int v2) {
return edges[v1][v2];
}
//显示图对应的矩阵
public void showGraph() {
for (int[] link : edges) {
System.out.println(Arrays.toString(link));
}
}
}
完成代码2:
package com.atguigu.exer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
/**
* @author WZ
* @create 2021-11-30 23:13
*/
public class GraphExer2 {
public static void main(String[] args) {
GraphExer2 graph=new GraphExer2(5);
String[] s1=new String[]{"A","B","C","D","E"};
for (int i = 0; i < s1.length; i++) {
graph.addDot(s1[i]);
}
graph.addBian(0,1,1);
graph.addBian(0,2,1);
graph.addBian(1,0,1);
graph.addBian(1,2,1);
graph.addBian(1,3,1);
graph.addBian(1,4,1);
graph.show();
graph.dfs();
System.out.println();
System.out.println("广度优先");
graph.bfs();
}
//属性
private ArrayList<String> listDot;
private int[][] arrDot;
private int numofBian;
private boolean[] isFind;
//构造器
public GraphExer2(int n){
listDot=new ArrayList(n);
arrDot=new int[n][n];
numofBian=0;
}
//结点的添加方法
public void addDot(String s){
listDot.add(s);
}
//添加边的方法
public void addBian(int v1,int v2,int weight){
arrDot[v1][v2]=weight;
arrDot[v2][v1]=weight;
numofBian++;
}
//根据i得到相应的值的方法
public String getDot(int i){
return listDot.get(i);
}
//得到边的数目的方法
public int getNumofBian(){
return numofBian;
}
//得到结点数目的方法
public int getNumofDot(){
return listDot.size();
}
//数组的遍历
public void show(){
for (int[] ints : arrDot) {
System.out.println(Arrays.toString(ints));
}
}
//得到邻接点w的方法
public int getW(int index){
for (int j = 0; j < getNumofDot(); j++) {
if (arrDot[index][j]>0){
return j;
}
}
return -1;
}
//的到相邻数据的方法
public int getNight(int v1,int v2){
for (int j = v2+1; j < getNumofDot(); j++) {
if (arrDot[v1][v2]>0){
return j;
}
}
return -1;
}
//深度遍历的方法
public void dfs(boolean[] isFind,int i){
System.out.print(getDot(i));
isFind[i]=true;
int w=getW(i);
if (w!=-1){
if (!isFind[w]){
dfs(isFind,w);
}
w=getNight(i,w);
}
}
//对深度遍历进行重载
public void dfs(){
isFind=new boolean[5];
for (int i = 0; i < getNumofDot(); i++) {
if (!isFind[i]){
dfs(isFind,i);
}
}
}
//广度优先遍历的算法
public void bfs(boolean[] isFind,int i){
int u;
int w;
LinkedList queue=new LinkedList();
System.out.println(getDot(i));
isFind[i]=true;
queue.addLast(i);
while (!queue.isEmpty()){
u=(Integer)queue.removeFirst();
w=getW(u);
while (w!=-1){
if (!isFind[w]){
System.out.println(getDot(w));
isFind[w]=true;
queue.addLast(w);
}
w=getNight(u,w);
}
}
}
public void bfs(){
isFind=new boolean[5];
for (int i = 0; i < getNumofDot(); i++) {
if (!isFind[i]){
bfs(isFind,i);
}
}
}
}
|