广度优先遍历的概念就不多加叙述了,简而言之就是访问一个顶点,然后访问完这个顶点的所有邻接顶点,再访问每一个邻接顶点的所有邻接顶点,为了实现这个方法,必须使用辅助队列,之前对于邻接数组下的遍历已经介绍过,这里是邻接表实现的图,直接上代码了。
java代码实现
package mypackage;
import javax.print.DocFlavor;
import java.util.Iterator;
//队列类,用链表实现,后面有用
class Queue<T> implements Iterable<T>{
// 节点个数,头节点,尾节点
private int N;
private Node head;
private Node last;
//节点类
public class Node {
public T data;
public Node next;
public Node(T data, Node next) {
this.data = data;
this.next = next;
}
}
//构造方法,初始化
public Queue() {
this.N = 0;
this.head = new Node(null,null);
this.last = null;
}
//队列长度
public int size(){
return N;
}
//队列是否为空
public boolean isEmpty(){
return N==0;
}
//入队列
public void enqueue(T data){
// 如果队列为空,说明尾节点为空,让新节点为尾节点,头借点指向尾节点
if (isEmpty()){
last=new Node(data,null);
head.next=last;
// 如果队列不为空,让新节点为尾节点,老的尾节点指向新尾节点
}else {
Node oldlast=last;
last=new Node(data,null);
oldlast.next=last;
}
// 最后元素+1
N++;
}
//出队列,注意先入先出,每次出的节点就是head指向的第一个节点,然后让head只想第二个节点即可
// 且注意,如果队列为空,要将last=null
public T dequeue(){
// 如果为空,返回null
if (isEmpty()){
return null;
// 如果不为空,让head只想第二个节点,元素-1,且如果队列为空,要将last=null
}else {
Node oldfirst=head.next;
head.next=oldfirst.next;
N--;
if (isEmpty()){
last=null;
}
// 返回弹出的元素
return oldfirst.data;
}
}
// 遍历
@Override
public Iterator iterator() {
return new QIterator();
}
// 创建一个内部类实现Iterator接口
public class QIterator implements Iterator {
// 定义一个遍历的节点
private Node n;
public QIterator() {
// 初始化为0索引位置
this.n = head;
}
//重写两个方法
@Override
public boolean hasNext() {
// 这个方法判断是否超出最大索引,如果超出会停止遍历
return n.next != null;
}
@Override
public Object next() {
// 这个方法会遍历得每个节点
n = n.next;
return n.data;
}
}
}
class Graph<T> {
private int V;//顶点数
private int E;//边数
private Queue<T>[] adj;//邻接表,用于装每个顶点的相连的顶点组成的队列,注意索引是每个顶点,这个索引对应的值是相连的顶点组成的队列
//adj中索引是每个顶点,为了在图中可以添加任意类型的数据,比如城市名字,字母等,因此使用一个数组将索引映射为其他数据
//比如讲索引0,映射为A
private T[] mapArr;
private boolean[] isVisited;//是否被访问过
private Queue<T> temp;
//构造方法,传入顶点个数,初始化边数为0,
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 Queue<T>();//初始化数组的每个队列
}
//初始化mapArr,在初始情况下,每个索引对应的顶点元素为null
this.mapArr = (T[]) new Object[v];//注意不能new T[v]
for (int i = 0; i < mapArr.length; i++) {
mapArr[i] = null;
}
}
//添加顶点,和索引对应
public void addV(int i, T v) {
mapArr[i] = v;
}
//获得顶点的个数
public int getV() {
return V;
}
//获取顶点在邻接表内所对应的索引
public int getIndex(T v) {
for (int i = 0; i < mapArr.length; i++) {
if (mapArr[i] == v) {
return i;
}
}
return -1;
}
//添加边,即将顶点v,w连接起来形成图,注意是无向的因此不仅要adj[getIndex(v)].enqueue(w);还要adj[getIndex(w)].enqueue(v);
public void addEdge(T v, T w) {
adj[getIndex(v)].enqueue(w);
adj[getIndex(w)].enqueue(v);
E++;//边数+1
}
// 获取边的个数
public int getE() {
return E;
}
//获取某个顶点相邻的所有顶点,返回这个队列即可
public Queue<T> getAdj(T v) {
return adj[getIndex(v)];
}
//广度优先遍历对外调用方法
public void wideSerch() {
temp=new Queue<T>();
//初始化均为没有被访问过
isVisited = new boolean[getV()];
for (int i = 0; i < isVisited.length; i++) {
isVisited[i] = false;
}
//对每一个顶点都优先广度遍历
for (int i = 0; i < getV(); i++) {
if (isVisited[i] == false) {
wideSerch(mapArr[i]);
}
}
}
//广度优先遍历重载方法,让第一个顶点入队列,标记为已经访问,打印出来
//循环队列,先弹出一个顶点,以这个顶点为基础,访问他的邻接表,并且把这些顶点装入队列,以此类推
//总之,核心思想是,先访问一个顶点的所有相邻顶点,然后再访问每个相邻顶点的相邻顶点
public void wideSerch(T v) {
isVisited[getIndex(v)] = true;
temp.enqueue(v);
System.out.print(v + "-->");
while (!temp.isEmpty()){
T out=temp.dequeue();
for (T q : adj[getIndex(out)]) {
if (isVisited[getIndex(q)] == false) {
isVisited[getIndex(q)] = true;
temp.enqueue(q);
System.out.print(q + "-->");
}
}
}
}
}
//测试
public class MyJava {
public static void main(String[] args) {
Graph <String>graph=new Graph(7);
//添加顶点
graph.addV(0,"A");
graph.addV(1,"B");
graph.addV(2,"C");
graph.addV(3,"D");
graph.addV(4,"E");
graph.addV(5,"F");
graph.addV(6,"G");
//添加边
graph.addEdge("A","E");
graph.addEdge("A","F");
graph.addEdge("A","G");
graph.addEdge("E","B");
graph.addEdge("F","C");
graph.addEdge("G","D");
System.out.println("顶点个数:"+graph.getV());
System.out.println("边条数:"+graph.getE());
System.out.print("顶点A相邻元素包括:");
Queue vs=graph.getAdj("A");
for (Object v : vs){
System.out.print(v+" ");
}
System.out.println();
System.out.println("广度优先遍历图:");
graph.wideSerch();
}
}