图结构——数据结构与算法学习
图
为何要有图呢?
其实就是对于线性表和树的补充,线性表和树只能表示一对一的关系,而图可以表示多对多的关系。
关于图的基本解释:
图的两种表示方式
- 邻接矩阵
- 邻接表
图的两种搜索方式
辅助理解资源:
深度优先搜索
原理:
首先把一条路径上的纵向节点挖掘完毕,然后再进行横向访问。(借助递归实现)
具体步骤:
1) 访问初始结点 v,并标记结点 v 为已访问。
2) 查找结点 v 的第一个邻接结点 w。
3) 若 w 存在,则继续执行 4,如果 w 不存在,则回到第 1 步,将从 v 的下一个结点继续。
4) 若 w 未被访问,对 w 进行深度优先遍历递归(即把 w 当做另一个 v,然后进行步骤 123)。
5) 查找结点 v 的 w 邻接结点的下一个邻接结点,转到步骤 3。
广度优先搜索
原理:
先找到一个节点的全部邻接节点,再根据节点的顺序依次遍历。(使用队列进行完成)
具体步骤:
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。
深度优先vs广度优先
代码实现
package Graph;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Stack;
public class Graph {
//因为此时没有被实例化,在内存中是不存在的
private ArrayList<String> vertexlist;//存储顶点集合
private int edges[][];//图对应的凝结矩阵
private int numOfEdges;//定义边的数目
public boolean[] isVisited;//定义是否被访问的数组
public static void main(String[] args) {
int n = 8;
String Vertexs[] = {"1","2","3","4","5","6","7","8"};
Graph graph = new Graph(n);
for (String vertex : Vertexs) {
graph.insertVertex(vertex);
}
//更新边的关系
graph.insertEdge(0, 1, 1);
graph.insertEdge(0, 2, 1);
graph.insertEdge(1, 3, 1);
graph.insertEdge(1, 4, 1);
graph.insertEdge(3, 7, 1);
graph.insertEdge(4, 7, 1);
graph.insertEdge(2, 5, 1);
graph.insertEdge(2, 6, 1);
graph.insertEdge(5, 6, 1);
graph.showGraph();
System.out.println("深度遍历");
graph.dfs();
System.out.println();
System.out.println("广度遍历");
graph.bfs();
System.out.println("test");
System.out.println("广度测试");
graph.dfs2();
}
public Graph(int n){
edges = new int[n][n];
vertexlist = new ArrayList<String>(n);
numOfEdges = 0;
}
//图中常用的方法
public int getNumOfVertex(){
return vertexlist.size();
}
//显示图对应的矩阵
public void showGraph(){
for (int[] edge : edges) {
System.out.println(Arrays.toString(edge));
}
}
//得到边的数目
public int getNumOfEdges(){
return numOfEdges;
}
//返回节点下标对应的数据
public String getValueByIndex(int i){
return vertexlist.get(i);
}
//返回v1 和 v2的权值
public int getWeight(int v1,int v2){
return edges[v1][v2];
}
//插入结点
public void insertVertex(String vertex){
vertexlist.add(vertex);
}
//添加边
public void insertEdge(int v1,int v2,int weight){
edges[v1][v2] = weight;
edges[v2][v1] = weight;
numOfEdges++;
}
//根据索引得到第一个凝结结点的下标
public int getFirstNeighbor(int index){
for (int i = 0; i < vertexlist.size(); i++) {//从0开始确保最先得到索引为0的节点
if(edges[index][i] > 0){
return i;
}
}
return -1;
}
//根据前一个凝结结点的下标来获取下一个凝结结点
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;
}
//深度优先遍历算法
public void dfs(boolean[] visited,int i){
System.out.print(getValueByIndex(i) + "->");
isVisited[i] = true;
int w = getFirstNeighbor(i);
while(w != -1){
if(!isVisited[w]){
dfs(isVisited,w);
}
w = getNextNeighbor(i,w);//一个节点遍历完转向下一个节点
}
}
//对每一个节点为初始节点的情况都进行遍历,考虑到费连通图的情况
public void dfs(){
isVisited = new boolean[vertexlist.size()];
for (int i = 0; i < getNumOfVertex(); i++) {
if(!isVisited[i]){
dfs(isVisited,i);
}
}
}
//广度优先遍历算法
private void bfs(boolean[] isVisited,int i){
int u;
int w;
LinkedList linkedList = new LinkedList();
System.out.print(getValueByIndex(i)+ "->");
isVisited[i] = true;
linkedList.addLast(i);
while(!linkedList.isEmpty()){
u = (Integer)linkedList.removeFirst();
w = getFirstNeighbor(u);
while(w != -1){
if(!isVisited[w]){
System.out.print(getValueByIndex(w) +"->");
isVisited[w] = true;
linkedList.addLast(w);
}
w = getNextNeighbor(u,w);
}
}
}
public void bfs() {
isVisited = new boolean[vertexlist.size()];
for(int i = 0; i < getNumOfVertex(); i++) {
if(!isVisited[i]) {
bfs(isVisited, i);
}
}
}
//使用栈结构实现深度优先遍历(将C语言改成java语言)
public void dfs_stack(boolean[] visited,int i){
System.out.println(getValueByIndex(i) + "->");
isVisited[i] = true;
Stack<Integer> stack = new Stack<Integer>();//循环时用来放节点,类似于递归时的向上return
int w = getFirstNeighbor(i);
int k,v;
k = i;//表示父节点的代指
v = w;//v代表的是i的临界节点
while(v != -1){
while(true){
w = getFirstNeighbor(k);//表示得到第一个初始节点
if(w == -1){
break;
}
k = w;
System.out.println(getValueByIndex(w)+"-");
}
v = getNextNeighbor(i,v);
}
}
//对每一个节点为初始节点的情况都进行遍历,考虑到费连通图的情况
public void dfs2(){
isVisited = new boolean[vertexlist.size()];
for (int i = 0; i < 1; i++) {
if(!isVisited[i]){
dfs(isVisited,i);
}
}
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!