深度优先搜索与广度优先搜索
小弟初来咋到,面对算法一脸懵逼,希望各位大神能够给予指点,在此谢过。
无向图
图是由一组定点和一组能够将两个顶点连接的边组成的一种数据结构
邻接表是图的一种存储方式,使用图的节点做索引,节点的相邻节点的集合为值
无向图:
无向图的邻接表:
/**
* 无向图
* Created by Fearless on 2017/4/18.
*/
public class Graph {
private final int V;//节点数
private int E;//边数
private Bag<Integer>[] adj;//Bag用来存储节点的相邻节点
public Graph(int v) {
V = v;
E = 0;
adj = new Bag[V];
//System.out.println(Arrays.toString(adj));
for (int i = 0; i < V; ++i)
adj[i] = new Bag<>();
//System.out.print(Arrays.toString(adj));
}
public int getV() {
return V;
}
public int getE() {
return E;
}
//添加边
public void addEdge(int v, int w) {
//分别添加到对方的定点集合里,边数加一
adj[v].add(w);
adj[w].add(v);
E++;
}
/***
* 获取相邻点,相邻点就是在adj[v]中存入的所有点
* @param v
* @return
*/
public Iterable<Integer> adj(int v) {
return adj[v];
}
@Override
public String toString() {
String s = V + " vertices, " + E + " edges\n";
for (int v = 0; v < V; v++) {
s += v + ":";
for (int w : this.adj(v)) {
s = w + " ";
}
s = s + "\n";
}
return s;
}
}
import java.util.Iterator;
import java.util.NoSuchElementException;
/**
* 包(如果从first取内容就是栈)
* Created by Fearless on 2017/4/18.
*/
public class Bag<T> implements Iterable<T> {
private Node<T> first; //首节点
private int n; //长度
private static class Node<T> {
private T item;
private Node<T> next;
}
public Bag() {
first = null;
n = 0;
}
public boolean isEmpty() {
return first == null;
}
public int size() {
return n;
}
public void add(T item) {
Node<T> oldfirst = first;
first = new Node<T>();
first.item = item;
first.next = oldfirst;
n++;
}
public Iterator<T> iterator() {
return new BagIterator<T>(first);
}
private class BagIterator<T> implements Iterator<T> {
private Node<T> current;
public BagIterator(Node<T> first) {
current = first;
}
public boolean hasNext() { return current != null; }
public void remove() { throw new UnsupportedOperationException(); }
public T next() {
if (!hasNext()) throw new NoSuchElementException();
T item = current.item;
current = current.next;
return item;
}
}
}
深度优先搜索
深度优先搜索是基于邻接表的一种检索方式,通过遍历头结点中的所有相邻节点,以及相邻节点中的节点来查找到需要找的节点
核心代码:
public class Search{
private boolean[] marked;//用来标记定点是否已经搜索
public Search(Graph g,int s){
marked=new boolean[g.getV()];
depthFirstSearch(g, s);
}
/***
* 深度优先搜索
* @param g 需要搜索的图
* @param v 搜索的开始节点
*/
private void depthFirstSearch(Graph g, int v) {
marked[v]=true;//标记点已被搜索
//获取v点的所有相邻的点,并遍历
for(Integer i:g.adj(v)){
//如果没有已搜索标记,对其进行搜索
if(!marked[i])
depthFirstSearch(g,i);
}
}
}
使用深度优先搜索遍历路径
import java.util.Stack;
/**
* 使用深度优先搜索遍历路径
* Created by Fearless on 2017/4/19.
*/
public class DepthFirstPaths {
private boolean[] marked;//记录节点是否已经被搜索
//edgeTo[a]=b a-b 为 s到a的已知的最后一条边,为了顶点a能够快速找到通往s的上一个节点
private int[] edgeTo;
private final int s;
public DepthFirstPaths(Graph g,int s){
this.s=s;
marked=new boolean[g.getV()];//初始化标记数组(默认值为false)
edgeTo=new int[g.getV()];//初始化数组
//进行搜索
depthFirstSearch(g,s);
}
/***
* 深度优先搜索
* @param g 需要搜索的图
* @param v 搜索的开始节点
*/
private void depthFirstSearch(Graph g, int v) {
marked[v]=true;//标记点已被搜索到
//获取v点的所有相邻的点,并遍历
for(Integer i:g.adj(v)){
//如果没有已搜索标记,对其进行搜索
if(!marked[i]){
edgeTo[i]=v;
depthFirstSearch(g,i);
}
}
}
public Iterable<Integer> pathTo(int v){
if(!hasPathTo(v)) return null;
Stack<Integer> path=new Stack<>();
for(int i=v;i!=s;i=edgeTo[i])
//把能够连通的顶点按照从后往前的连接顺序压入栈中
path.push(i);
path.push(s);
return path;
}
/**
* 是否有节点s到该节点v的路径
* @param v
* @return
*/
private boolean hasPathTo(int v) {
return marked[v];//如果搜索过就会是true
}
}
class DepthFirstPathsDemo{
public static void main(String ... args) {
/**
* 测试数据
*/
Graph g = new Graph(6);
g.addEdge(0, 5);
g.addEdge(2, 4);
g.addEdge(2, 3);
g.addEdge(1, 2);
g.addEdge(0, 1);
g.addEdge(3, 4);
g.addEdge(3, 5);
g.addEdge(0, 2);
DepthFirstPaths d;
Stack<Integer> s;
int len=g.getV();
for (int i = 0; i < len; ++i) {
System.out.println("\n顶点为"+i+"的深度优先遍历");
d = new DepthFirstPaths(g, i);
for(int j=0;j<len;++j) {
System.out.print(i+"->"+j+"的路径:");
s = (Stack<Integer>) d.pathTo(j);
int l = s.size();
for (int k = 0; k < l; ++k) {
if(k!=0) System.out.print("-->");
System.out.print(s.pop());
}
System.out.println();
}
}
}
}
广度优先搜索
广度优先搜索就是,先将头结点的相邻节点集合a遍历结束后,再去遍历a的相邻节点
通过广度优先搜索能够很容易的获取到最短路径
如图,会先遍历1,2,5 完后会依次遍历2,1,5 的相邻节点
使用广度优先搜索查找最短路径
import java.util.LinkedList;
import java.util.Queue;
import java.util.Stack;
/**
* 使用广度优先搜索查找最短路径
* Created by Fearless on 2017/4/19.
*/
public class BreadthFirstPaths {
private boolean[] marked;
private int[] edgeTo;
private final int s;
public BreadthFirstPaths(Graph g,int s){
marked=new boolean[g.getV()];
edgeTo=new int[g.getV()];
this.s=s;
bfs(g,s);
}
/**
* 广度优先遍历
* 1.将首个节点放入队列
* 2.从队列中取第一个节点,并将其 相邻的未遍历过的节点 标记为已遍历后将放入队列中
* 3.重复步骤2,直到队列为空
* @param g
* @param s
*/
private void bfs(Graph g, int s) {
marked[s]=true;
Queue<Integer> q=new LinkedList<>();
q.offer(s);
while (!q.isEmpty()){
int v=q.poll();
for(int i:g.adj(v)){
if(!marked[i]){
marked[i]=true;
edgeTo[i]=v;
q.offer(i);
}
}
}
}
public boolean hasPathTo(int v){
return marked[v];
}
public Iterable<Integer> pathTo(int v){
if(!hasPathTo(v)) return null;
Stack<Integer> e=new Stack<>();
for(int i=v;i!=s;i=edgeTo[i])
e.push(i);
e.push(s);
return e;
}
}
class BreadthFirstPathsDemo{
public static void main(String ... args){
/**
* 测试数据
*/
Graph g = new Graph(6);
g.addEdge(0, 5);
g.addEdge(2, 4);
g.addEdge(2, 3);
g.addEdge(1, 2);
g.addEdge(0, 1);
g.addEdge(3, 4);
g.addEdge(3, 5);
g.addEdge(0, 2);
BreadthFirstPaths d;
Stack<Integer> s;
int len=g.getV();
for (int i = 0; i < len; ++i) {
System.out.println("\n顶点点为"+i+"的广度优先遍历");
d = new BreadthFirstPaths(g, i);
for(int j=0;j<len;++j) {
System.out.print(i+"->"+j+"的最短路径:");
s = (Stack<Integer>) d.pathTo(j);
int l = s.size();
for (int k = 0; k < l; ++k) {
if(k!=0) System.out.print("-->");
System.out.print(s.pop());
}
System.out.println();
}
}
}
}