无向图
无向图的实现
使用邻接表实现:
public class Graph {
//记录顶点数量
private int V;
//记录边数量
private int E;
//领接表
private Queue<Integer>[] adj;
public Graph(int V){
this.V=V;
this.E=0;
this.adj=new Queue[V];
for(int i=0;i<adj.length;i++){
adj[i]=new LinkedList<Integer>();
}
}
//获取图中顶点的数量
public int V(){
return V;
}
//获取图中边的数量
public int E(){
return E;
}
//向图中添加一条边v-w
public void addEdge(int v,int w){
//把w添加到v的链表中,这样顶点v就多了一个相邻点w
adj[v].offer(w);
//把v添加到w的链表中,这样顶点w就多了一个相邻点v
adj[w].offer(v);
//边的数目+1
E++;
}
//获取和顶点v相邻的所有顶点
public Queue<Integer> adj(int v){
return adj[v];
}
}
无向图的搜索
深度优先搜索
1.原理
深度优先搜索,指的是在搜索时,如果遇到一个结点既有子结点,又有兄弟结点,那么先找子结点,然后找兄弟结点。
在由于边是没有方向的,所以,如果4和5顶点相连,那么4会出现在5的相邻链表中,5也会出现在4的相邻链表中,那么为了不对顶点进行重复搜索,应该要有相应的标记来表示当前顶点有没有搜索过,可以使用一个布尔类型的数组 boolean[V] marked,索引代表顶点,值代表当前顶点是否已经搜索,如果已经搜索,标记为true,如果没有搜索,标记为false;
2.代码实现
public class DepthFirstSearch {
//索引代表顶点,值表示当前顶点是否已经被搜索
private boolean[] marked;
//记录有多少个顶点与s顶点相通
private int count;
//构造深度优先搜索对象,使用深度优先搜索找出G图中s顶点的所有相通顶点
public DepthFirstSearch(Graph G,int s){
//创建一个与图的顶点数一样大小的布尔数组
marked=new boolean[G.V()];
//搜索G图中与s顶点相通的所有顶点
count=0;
dfs(G,s);
}
//使用深度优先搜索找出G图中v顶点的所有相通顶点
private void dfs(Graph G,int v){
//把当前顶点标记为已搜索
marked[v]=true;
//遍历v顶点的领接表,得到每一个顶点w
for(Integer w: G.adj(v)){
//如果当前顶点w没有被搜索过,则递归搜索与w顶点相通的其他顶点
if(!marked(w)){
dfs(G,w);
}
}
//相遇的顶点数量+1
count++;
}
//判断w顶点与s顶点是否相通
public boolean marked(int w){
return marked[w];
}
//获取与顶点s相通的所有顶点的总数
public int count(){
return count;
}
}
//测试代码
public class DepthFirstSearchTest {
public static void main(String[] args){
//创建Graph对象
Graph G=new Graph(13);
G.addEdge(0,1);
G.addEdge(0,2);
G.addEdge(0,5);
G.addEdge(0,6);
G.addEdge(3,5);
G.addEdge(3,4);
G.addEdge(4,5);
G.addEdge(4,6);
G.addEdge(7,8);
G.addEdge(9,10);
G.addEdge(9,11);
G.addEdge(9,12);
G.addEdge(11,12);
DepthFirstSearch search=new DepthFirstSearch(G,0);
int count = search.count();
System.out.println("与0相通的顶点的数量:"+count);
boolean marked = search.marked(7);
System.out.println("顶点7与是否与0相通:"+marked);
}
}
广度优先搜索
1.原理
2.代码实现
ublic class BreadthFirstSearch {
//索引代表顶点,值表示当前顶点是否已经被搜索
private boolean[] marked;
//记录有多少个顶点与s顶点相遇
private int count;
//用来存储待搜索领接表的点
private Queue<Integer> waitSearch;
//构造广度优先搜索对象,使用广度优先搜索找出G图中s顶点的所有相邻顶点
public BreadthFirstSearch(Graph G,int s){
marked=new boolean[G.V()];
count=0;
waitSearch=new LinkedList<>();
bfs(G,s);
}
//使用广度优先搜索找出G图中v顶点的所有相邻顶点
private void bfs(Graph G,int v){
//把当前顶点v标记为已搜索
marked[v]=true;
//把当前顶点v放入到队列中,等待搜索它的领接表
waitSearch.offer(v);
//使用while循环从队列中拿出待搜索的顶点wait,进行搜索邻接表
while(!waitSearch.isEmpty()){
Integer wait = waitSearch.poll();
//遍历wait顶点的邻接表,得到每一个顶点w
Queue<Integer> adj = G.adj(wait);
while(adj.size()>0){
Integer w=adj.poll();
if(!marked[w]){
marked[w]=true;
waitSearch.offer(w);
}
}
count++;
}
}
//判断w顶点与s顶点是否相通
public boolean marked(int w){
return marked[w];
}
//获取与顶点s相通的所有顶点的总数
public int count(){
return count;
}
}
//测试代码
public class BreadthFirstSearchTest {
public static void main(String[] args){
Graph G=new Graph(13);
G.addEdge(0,1);
G.addEdge(0,2);
G.addEdge(0,5);
G.addEdge(0,6);
G.addEdge(3,5);
G.addEdge(3,4);
G.addEdge(4,5);
G.addEdge(4,6);
G.addEdge(7,8);
G.addEdge(9,10);
G.addEdge(9,11);
G.addEdge(9,12);
G.addEdge(11,12);
BreadthFirstSearch search=new BreadthFirstSearch(G,9);
int count = search.count();
System.out.println("与9相通的顶点的数量:"+count);
boolean marked = search.marked(7);
System.out.println("顶点7与是否与0相通:"+marked);
}
}
路径查找
1.实现介绍
我们实现路径查找,最基本的操作还是得遍历并搜索图,所以,我们的实现暂且基于深度优先搜索来完成。其搜索的过程是比较简单的。我们添加了edgeTo[]整型数组,这个整型数组会记录从每个顶点回到起点s的路径。如果我们把顶点设定为0,那么它的搜索可以表示为下图:
2.代码实现
public class DepthFirstPaths {
//索引代表顶点,值表示当前顶点是否已经被搜索
private boolean[] marked;
//起点
private int s;
//索引代表顶点,值代表从起点s到当前顶点路径上的最后一个顶点
private int[] edgeTo;
//构造深度优先搜索对象,使用深度优先搜索找出G图中起点为s的所有路径
public DepthFirstPaths(Graph G,int s){
marked=new boolean[G.V()];
this.s=s;
edgeTo=new int[G.V()];
dfs(G,s);
}
//使用深度优先先搜索找出G图中v顶点的所有相邻顶点
private void dfs(Graph G,int v){
marked[v]=true;
for(Integer w:G.adj(v)){
if(!marked[w]){
edgeTo[w]=v;
dfs(G,w);
}
}
}
//判断v顶点与s顶点是否存在路径
public boolean hasPathTo(int v){
return marked[v];
}
//找出从起点s到顶点v的路径(就是该路径经过的顶点)
public Stack pathTo(int v){
//当前v顶点与s顶点不连通,所以直接返回null,没有路径
if(!hasPathTo(v)){
return null;
}
//创建路径中经过的顶点的容器
Stack<Integer> path=new Stack<>();
//第一次把当前顶点存进去,然后将x变换为到达当前顶点的前一个顶点edgeTo[x],在把前一个顶点存进去,
// 继续将x变化为到达前一个顶点的前一个顶点,继续存,一直到x的值为s为止,相当于逆推法,最后把s放进去
for(int x=v;x!=s;x=edgeTo[x]){
path.push(x);
}
//把起点s放入容器中
path.push(s);
return path;
}
}
//测试代码
public class DepthFirstPathsTest {
public static void main(String[] args){
Graph G=new Graph(6);
G.addEdge(0,1);
G.addEdge(0,2);
G.addEdge(0,5);
G.addEdge(1,2);
G.addEdge(2,3);
G.addEdge(2,4);
G.addEdge(3,4);
G.addEdge(3,5);
DepthFirstPaths paths=new DepthFirstPaths(G,0);
Stack<Integer> stack = paths.pathTo(2);
while(!stack.isEmpty()){
Integer pop = stack.pop();
System.out.print(pop+"-");
}
}
}
有向图
有向图的实现
public class Digraph {
//记录顶点数量
private int V;
//记录边数量
private int E;
//邻接表
private Queue<Integer>[] adj;
//创建一个包含V个顶点但不包含边的有向图
public Digraph(int V){
this.V=V;
this.E=0;
adj=new Queue[V];
for(int i=0;i<adj.length;i++){
adj[i]=new LinkedList<Integer>();
}
}
//获取图中顶点的数量
public int V(){
return V;
}
//获取图中边的数量
public int E(){
return E;
}
//向有向图中添加一条边v-w
public void addEdge(int v,int w){
adj[v].offer(w);
E++;
}
//获取由v指出的边所连接的所有顶点
public Queue<Integer> adj(int v){
return adj[v];
}
//该图的反向图
public Digraph reverse(){
//创建新的有向图对象
Digraph r=new Digraph(V);
//遍历0-V-1的所有顶点,拿到每一个顶点v
for(int v=0;v<V;v++){
//得到原图中的v顶点对应的领接表,原图中的v-w,则反向图为w-v
for(Integer w:adj(v)){
r.addEdge(w, v);
}
}
return r;
}
}
加权无向图
加权无向图中边的表示
加权无向图中的边我们就不能简单的使用v-w两个顶点表示了,而必须要给边关联一个权重值,因此我们可以使用对象来描述一条边
public class Edge implements Comparable<Edge>{
//顶点1
private int v;
//顶点2
private int w;
//当前边的权重值
private double weight;
//通过顶点v和w,以及权重weight值构造一个边对象
public Edge(int v,int w,double weight){
this.v=v;
this.w=w;
this.weight=weight;
}
//获取边上的权重值
public double weight(){
return weight;
}
//获取边上的一个点
public int either(){
return v;
}
//获取边上除了顶点vertex外的另外一个顶点
public int other(int vertex){
if(vertex==v){
return w;
}else{
return v;
}
}
//比较当前边和参数that边的权重,如果当前边权重大,则返回1
//如果一样大,返回0 如果当前边权重小,返回-1
public int compareTo(Edge that){
int cmp;
if(this.weight()>that.weight()){
cmp=1;
}else if(this.weight()<that.weight()){
cmp=-1;
}else{
cmp=0;
}
return cmp;
}
}
加权无向图的实现
public class EdgeWeightedGraph {
//记录顶点数量
private int V;
//记录边数量
private int E;
//邻接表
private Queue<Edge>[] adj;
//创建一个含有V个顶点的空加权无向图
public EdgeWeightedGraph(int V){
this.V=V;
this.E=0;
this.adj=new Queue[V];
for(int i=0;i<adj.length;i++){
adj[i]=new LinkedList<Edge>();
}
}
//获取图中顶点的数量
public int V(){
return V;
}
//获取图中边的数量
public int E(){
return E;
}
//向加权无向图中添加一条边e
public void addEdge(Edge e){
//获取边上的一个顶点
int v=e.either();
//获取边上的另外一个顶点
int w=e.other(v);
//因为是无向图,所以边e需要同时出现在两个顶点的邻接表中
adj[v].offer(e);
adj[w].offer(e);
//边的数量+1
E++;
}
//获取和顶点v关联的所有边
public Queue<Edge> adj(int v){
return adj[v];
}
//获取加权无向图的所有边
public Queue<Edge> edges(){
//创建一个队列,存储所有的边
Queue<Edge> allEdge=new LinkedList<>();
//遍历顶点,拿到每个顶点的邻接表
for(int v=0;v<V;v++){
//遍历邻接表,拿到邻接表中每条边
for(Edge e:adj(v)){
/*
无向图中,每条边对象Edge都会在两个顶点的邻接表中各出现一次,为了不重复获取,暂定:
除了当前顶点v,再获取边e中的另外一个顶点w,如果v<w则添加,这样可以保证同一条边
只会被统计一次
*/
if(e.other(v)<v){
allEdge.offer(e);
}
}
}
return allEdge;
}
}