图论基础
图的定义
是一种比线性表和树更为复杂的数据结构,在图形结构中结点之间的关系可以是任意的,任意两个数据元素都可能存在相关性,因此图论在计算中应用相当广泛,知识图谱,推荐算法。
图的基本元素
结点,顶点,弧(边),弧尾,弧头,有向图(边右箭头),无向图,权(边的权重),子图(递归),出度,入度,回路。
图的数据结构
根据不同情况选择不同的结构来表示
- 链表
- 数组 (V,E)
图的表示方法:
数组即邻接矩阵: 数组data[B][F]=10 (V,E)
邻接表:链表
B:->F->C->E
十字链表:为了更节约空间
多重邻接表 :
遍历方式
- BFS:广度搜索,非递归的方式
- DFS:深度搜索,递归的方式
BFS(广度搜索)
迷宫问题
有一天,小美和你去玩迷宫。但是方向感不好的小美很快就迷路了,你得知后便去解救无助的小美,你已经弄清楚了迷宫的地图,现在你要知道从你当前位置出发你是否能够达到
小美的位置?只可以前后左右走。
图的邻接矩阵如下:1表示障碍物,0表示没有障碍物,绿色代表当前我的位置,红色代表小美的位置。
0 0 1 0
0 0 0 0
0 0 1 0
0 1 0 0
0 0 0 1
BFS思路
我的起始位置(1,1)这点,目标位置(4,3)这点,我们要判断这两点能不能通,转成图论。
从我当前的点,每次判断前后左右能不能走,如果可以走我就加入我的可到达的点。
(1,1)=>(1,2),(2,1)=>(2,2),(3,1)
把可以到达的点加入待走的队列中,每次判断再把点拿出来,然后把判断可以走的点继续放入队列。
按照这个步骤可以判断出我能达到的任意一个点。
具体代码如下
class Point {
int x;
int y;
}
public class BFS {
// 定义一张图 我现在用矩阵的形式
private int n; // 表示行
private int m; // 表示列
private int dx; // 表示目标位置的x坐标
private int dy; // 表示目标位置的y坐标
private int data[][]; // 表示图的矩阵
private boolean mark[][]; // 用来标记我走过的路 防止死循环
public BFS(int n, int m, int dx, int dy, int[][] data, boolean[][] mark) {
super();
this.n = n;
this.m = m;
this.dx = dx;
this.dy = dy;
this.data = data;
this.mark = mark;
}
public void bfs(int startx, int starty) { // 表示我的起始位置
mark[startx][starty] = true; // 表示我已经走过的点
Queue<Point> queue = new ArrayBlockingQueue<>(n * m); // 队列的空间 有多少点就开多少
Point start = new Point();
start.x = startx;
start.y = starty;
queue.add(start); // 起始点加入队列
// 定义一个方向向量
int next[][] = { { 0, 1 }, { 1, 0 }, { 0, -1 }, { -1, 0 } }; //表示前后左右 二维数组
while (!queue.isEmpty()) { // 队列不为空 就表示还可以走
// 判断前后左右
Point curPoint = queue.poll(); // 每次取队列的头 出队列
for (int i = 0; i < 4; i++) {
int nextx = curPoint.x + next[i][0];
int nexty = curPoint.y + next[i][1];
// 表示走前后左右的点,其实就是坐标系运算 y+1表示往下走了一步 x+1表示往左走了一步
// 判断是否可以走
System.out.println("下一个点:" + nextx + ":" + nexty);
if (nextx < 1 || nextx > n || nexty < 1 || nexty > m)// 否则就出边了
continue;
if (!mark[nextx][nexty] && data[nextx][nexty] == 0) { // 判断有没有走过
// 表示可以走
// 判断是不是到了目的地
if (nextx == dx && nexty == dy) {// 表示是目的地
System.out.println("true");
return;
}
Point newPoint = new Point();
newPoint.x = nextx;
newPoint.y = nexty;
queue.add(newPoint); //下一步加入队列 就是一步一步走下去了
mark[nextx][nexty] = true;
}
}
}
System.out.println("false");
return;
}
public static void main(String[] args) {
Scanner cin = new Scanner(System.in);
int n = cin.nextInt();
int m = cin.nextInt();
int data[][] = new int[n + 1][m + 1];
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
data[i][j] = cin.nextInt();
}
}
int dx = cin.nextInt();
int dy = cin.nextInt();
boolean mark[][] = new boolean[n + 1][m + 1];
BFS bfs = new BFS(n, m, dx, dy, data, mark);
int startx = cin.nextInt();
int starty = cin.nextInt();
bfs.bfs(startx, starty);
}
}
// 0 0 1 0
// 0 0 0 0
// 0 0 1 0
// 0 1 0 0
// 0 0 0 1
DFS(深度搜索)
同样还是上面那个问题,只不过,这次需要找到最快的路径
0 0 1 0
0 0 0 0
0 0 1 0
0 1 0 0
0 0 0 1
一般牵涉到最快以及路径就考虑用DFS
DFS思路
就是找到一个方向就一直走下去,直到不能走了为止。不能走了就回退,到之前
剩下没有走的方向
如下图所示,从1找到5
代码如下
public class DFS {
// 定义一张图 我现在用矩阵的形式
private int n; // 表示行
private int m; // 表示列
private int dx; // 表示目标位置的x坐标
private int dy; // 表示目标位置的y坐标
private int data[][]; // 表示图的矩阵
private boolean mark[][]; // 用来标记我走过的路 防止死循环
ArrayList<Integer> steps; //存储每条路径的步数
// 定义一个方向向量
int next[][] = { { 0, 1 }, { 1, 0 }, { 0, -1 }, { -1, 0 } };
public DFS(int n, int m, int dx, int dy, int[][] data, boolean[][] mark) {
super();
this.n = n;
this.m = m;
this.dx = dx;
this.dy = dy;
this.data = data;
this.mark = mark;
this.steps = new ArrayList<>();
}
//问题:写出 从 startx starty 计算出到 dx dy最短需要走几步路,用递归
public void dfs(int x,int y,int step) { //x 和 y表示当前走到的点,step表示走到当前已经走了几步了
for (int[] ints : next){
int nextx = x + ints[0];
int nexty = y + ints[1];
//判断当前点是否能走
if (nextx < 1 || nextx > n || nexty < 1 || nexty > m || mark[nextx][nexty] || data[nextx][nexty] == 1){
continue;
}else {
System.out.print("("+nextx+","+nexty+")");
if (nextx == dx && nexty == dy){
steps.add(step);
System.out.println();
}else{
mark[x][y] = true;
dfs(nextx,nexty,++step);
step--;
mark[x][y] = false;
}
}
}
}
public static void main(String[] args) {
Scanner cin = new Scanner(System.in);
int n = cin.nextInt();
int m = cin.nextInt();
int data[][] = new int[n + 1][m + 1];
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
data[i][j] = cin.nextInt();
}
}
int dx = cin.nextInt();
int dy = cin.nextInt();
boolean mark[][] = new boolean[n + 1][m + 1];
DFS bfs = new DFS(n, m, dx, dy, data, mark);
int startx = cin.nextInt();
int starty = cin.nextInt();
问题:写出 从 startx starty 计算出到 dx dy最短需要走几步路,用递归
bfs.dfs(startx, starty,1);
for (Integer integer : bfs.steps){
System.out.println(integer);
}
}
}
// 0 0 1 0
// 0 0 0 0
// 0 0 1 0
// 0 1 0 0
// 0 0 0 1
应用场景
知识图谱:最推荐从这里如入手。
图形数据库 Neo4J。
推荐关系:猜你喜欢