算法-Java
算法
一、图
1.1.基础知识:
-
一个图G=<V,E>由两个集合来 定义:一个有限集合V,它的元素称为顶点(vertex);另一个有限集合E,它的元素是一对顶点,称为边(edge)。
-
G中的所有边都是无向的,我们称之为 无向图,若本身是有向的,则称为 有向图
-
对图的定义没有禁止圈(loop),即连接顶点自身的边,对于|V|个顶点的无圈无向图,它可能包含的边的数量|E|可以用这个不等式表示:
0≤|E|≤|V|(|V|–1)/2
(如果每个顶点|V|和所有其他|V|–1个顶点之间都有边相连,图中边的数量就会达到最大。但是,我们必须对|V|(|V|–1)的积除以2,因为每条边被包含了两次。)
-
任意两个顶点之间都有边相连的图称为 完全(complete)图。如果图中所缺的边数量相对较少,我们称它为 稠密(dense)图;如果图中的边相对顶点来说数量较少,我们称它为稀疏(sparse)图
-
矩阵元素A[i, j]可以简单地包含这条边的权重;当不存在这样一条边时,则包含一个特殊符号,例如∞。这种矩阵称为权重矩阵(weight matrix)或成本矩阵(cost matrix)。
2.2 .图的表示方法:
2.21.基础
- 图处理问题就是用哪种方式(数据结构)来表示图并实现这份API,这包含以下两个要求:
❏它必须为可能在应用中碰到的各种类型的图预留出足够的空间;
❏ Graph的实例方法的实现一定要快——它们是开发处理图的各种用例的基础。 - 表示方法:邻接矩阵与邻接表:
❏邻接矩阵。我们可以使用一个V乘V的布尔矩阵。当顶点v和顶点w之间有相连接的边时,定义v行w列的元素值为true,否则为false。这种表示方法不符合第一个条件——含有上百万个顶点的图是很常见的,V2个布尔值所需的空间是不能满足的。
❏邻接表数组。我们可以使用一个以顶点为索引的列表数组,其中的每个元素都是和该顶点相邻的顶点列表,参见图4.1.9。这种数据结构能够同时满足典型应用所需的以上两个条件
2.22.邻接矩阵数据结构:
性能特点:
❏使用的空间和V+E成正比;
❏添加一条边所需的时间为常数;
❏遍历顶点v的所有相邻顶点所需的时间和v的度数成正比(处理每个相邻顶点所需的时间为常数)。
Java代码实现:
public class Graph
{
private final int V; // 顶点数目
private int E; // 边的数目
private Bag<Integer>[] adj; // 邻接表
public Graph(int V)
{
this.V = V; this.E = 0;
adj = (Bag<Integer>[]) new Bag[V]; // 创建邻接表
for (int v = 0; v < V; v++) // 将所有链表初始化为空
adj[v] = new Bag<Integer>();
}
public Graph(In in)
{
this(in.readInt()); // 读取V并将图初始化
int E = in.readInt(); // 读取E
for (int i = 0; i < E; i++)
{ // 添加一条边
int v = in.readInt(); // 读取一个顶点
int w = in.readInt(); // 读取另一个顶点
addEdge(v, w); // 添加一条连接它们的边
}
}
public int V() { return V; }
public int E() { return E; }
public void addEdge(int v, int w)
{
adj[v].add(w); // 将w添加到v的链表中
adj[w].add(v); // 将v添加到w的链表中
E++;
}
public Iterable<Integer> adj(int v)
{ return adj[v]; }
}
这份Graph的实现使用了一个由顶点索引的整型链表数组。每条边都会出现两次,即当存在一条连接v与w的边时,w会出现在v的链表中,v也会出现在w的链表中。常见的应用场景都需要处理庞大的稀疏图,因此我们会一直使用邻接表。
2.23.深度优先与广度优先遍历:
图处理算法api:
深度优先搜索dfs:
-
深度优先和广度优先均为暴力法
-
在访问其中一个顶点时:
❏将它标记为已访问;❏递归地访问它的所有没有被标记过的邻居顶点。
-
在图中会路过每条边两次(在它的两个端点各一次)
-
伪码表示:
-
Java代码实现:
寻找路径:
- 路径的API:
-
使用深度优先搜索查找图中的路径
public class DepthFirstPaths { private boolean[] marked; // 这个顶点上调用过dfs()了吗? private int[] edgeTo; // 从起点到一个顶点的已知路径上的最后一个顶点 private final int s; // 起点 public DepthFirstPaths(Graph G, int s) { marked = new boolean[G.V()]; edgeTo = new int[G.V()]; this.s = s; dfs(G, s); } private void dfs(Graph G, int v) { marked[v] = true; for (int w : G.adj(v)) if (! marked[w]) { edgeTo[w] = v; dfs(G, w); } } public boolean hasPathTo(int v) { return marked[v]; } public Iterable<Integer> pathTo(int v) { if (! hasPathTo(v)) return null; Stack<Integer> path = new Stack<Integer>(); for (int x = v; x ! = s; x = edgeTo[x]) path.push(x); path.push(s); return path; } }

浙公网安备 33010602011771号