图的深度优先搜索
深度优先搜索算法(Depth-First-Search):它沿着树的深度遍历树的节点,尽可能深的搜索树的分支。当节点v的所有边都己被探寻过,搜索将回溯到发现节点v的那条边的起始节点。这一过程一直进行到已发现从源节点可达的所有节点为止。如果还存在未被发现的节点,则选择其中一个作为源节点并重复以上过程,整个进程反复进行直到所有节点都被访问为止。
那么深度优先搜索通过非递归来实现的思路:
①将顶点(第一个节点)压入栈中,同时将其标记已经被访问。
②查看栈顶的节点是否含有相邻的而且没有被访问过的节点,如果有,就将其压入栈中,否则就从栈中跳出,就行下面的图中H,没有相邻的节点,就将H从栈中跳出,从而栈顶再次变成了F,也好比B,虽然有相邻的节点F,但是F已经被访问过了,那么就在将B从栈中跳出,从而栈顶再变成了A。
③重复上面的操作,直到栈为空。
在这里两个节点是否相邻,通过邻接矩阵来实现。所谓的邻接矩阵,就是通过一个二维数组来表示,n行n列(n表示n个节点)。其中arr[i][j] = 1表示的示第i + 1个节点和第j + 1个节点是相邻的。
整个过程的分析:
代码实现:
定义节点
public class Node {
public char label;
public boolean wasVisited;//标记是否被访问
public Node(char ch){
label = ch;
wasVisited = false;
}
}
定义栈,并实现其相关的操作:
//栈(通过数组来实现栈的相关操作)
public class StackX {
public int[] arr;
public int max;//栈最多有max个元素
public int top;//栈顶
public StackX(int size){
arr = new int[size];
max = size;
top = -1;
}
/**
* 压栈(value表示第value + 1个节点)
* @param value
*/
public void push(int value){
if(top == max - 1)
throw new RuntimeException("只能为满,无法继续压栈");
arr[++top] = value;
}
//出栈
public int pop(){
if(isEmpty())
throw new RuntimeException("栈为空,无法出栈");
return arr[top--];
}
//查看栈顶的元素
public int peek(){
return arr[top];
}
//判断栈是否为空
public boolean isEmpty(){
return (top == -1);
}
}
深度优先搜索:
//实现深度优先搜索
public class DepthSearch {
public int[][] mat;//表示节点之间是否相邻
public Node[] arr;//表示一共有多少个节点
public StackX stack;
public int count;
public int size;
public DepthSearch(int max){
arr = new Node[max];
mat = new int[max][max];
stack = new StackX(max);
count = 0;
size = max;
}
/**
* 添加节点
* @param label
*/
public void addNode(char label){
if(count == size)
throw new RuntimeException("节点已经满了,无法继续插入");
arr[count++] = new Node(label);
}
/**
* 表示start ---end这个两个节点是相邻的,二维数组的值为1就是相邻的,否则为0为不相邻
* @param start
* @param end
*/
public void addAdjmat(int start, int end){
mat[start][end] = 1;
mat[end][start] = 1;
}
public void dfs(){
arr[0].wasVisited = true;//标记第一个节点被访问
stack.push(0);//首先将第一个节点压入栈中
System.out.print(arr[0].label+" ");//输出第一个节点的信息
while(!stack.isEmpty()){
//当栈没有空的时候,继续循环遍历
int current = stack.peek();//查看栈顶的节点
int temp = getUnvisitedNode(current);//查看第 current + 1个节点(栈顶的节点)是否有邻近的节点,而且没有被访问
if(temp == -1){
//如果temp 为-1,表示当前current这个节点没有邻近的节点,或者邻近的节点都已经被访问过了,那么就从栈
//中跳出,并输出其对应的信息
stack.pop();
}else{
//如果有这样一个节点,那么就将其标记为已经被访问过了,而且将其压入栈中
arr[temp].wasVisited = true;
stack.push(temp);
System.out.print(arr[temp].label +" ");
}
}
}
/**
* 找到和第 i + 1个节点相邻的而且没有被访问过的节点
* @param i
* @return
*/
public int getUnvisitedNode(int i){
for(int j = 0; j<size; j++){
if(mat[i][j] == 1 && arr[j].wasVisited == false){
return j;//如果当前节点和第 i + 1个节点是相邻的,而且没有被访问过,那么就返回j
}
}
return -1;//返回-1时表示当前第 i + 1个节点没有邻近的节点或者没有那些没有被访问过的节点
}
}
测试深度优先搜索:
//测试深度优先搜索
public class DepthSearchApp {
public static void main(String[] args){
DepthSearch depthSearch = new DepthSearch(5);
try {
depthSearch.addNode('A');//下标为0
depthSearch.addNode('B');//~~~1
depthSearch.addNode('C');//~~~2
depthSearch.addNode('D');//~~~3
depthSearch.addNode('E');//~~~4
depthSearch.addAdjmat(0, 1);
depthSearch.addAdjmat(1, 2);
depthSearch.addAdjmat(2, 3);
depthSearch.addAdjmat(3, 4);
depthSearch.dfs();
}catch (Exception e){
System.out.println(e.getMessage());
}
}
}
图的广度优先搜索
广度优先搜索算法(Breadth-First-Search):从根节点开始,沿着树(图)的宽度遍历树(图)的节点。如果所有节点均被访问,则算法中止。一般用队列数据结构来辅助实现BFS算法。
这里我用的是循环队列,当然也可以使用非循环队列来实现
那么判断循环队列是否为空的条件为 front == rear.(对于非循环队列来说是一样的)。
判断循环队列是否为满的条件为 (rear+1)%size == front
(而对于非循环队列来说,判断其为满,那么只要判断rear == size - 1即可)
过程:
①新建一个二位数组(邻接矩阵),表示两个节点是否相邻的关系;新建队列,同时新建一个一维数组arr,表示节点。
②调用方法,从而添加节点,同时添加节点之间的邻接关系。
③首先将第一个节点arr[0]压入到队列中,同时将其标记为已经被访问了
④然后将队头节点从队列中跳出,查看其是否含有相邻的而且没有被访问过的节点,如果有,就将这个相邻的没有被访问过的节点压入队列中,同时标记已经被访问过了。
③重复上面的操作④,直到队列为空。
图解:
代码实现:
定义节点:
//节点
public class Node {
public char label;
public boolean wasVisited;//标记是否被访问
public Node(char ch){
label = ch;
wasVisited = false;
}
}
实现队列的相关操作:
//队列
public class Queue {
public int[] arr;
public int size;
public int front;
public int rear;
public Queue(int max){
size = max;
arr = new int[max];
rear = 0;
front = 0;
}
/**
* 将节点插入到队列中
* @param value
*/
public void insert(int value){
if(isFull()){
throw new RuntimeException("队列为满,无法继续入队");
}
arr[rear++] = value;
rear %= size;
}
/**
* 将队头节点从队列中跳出
* @return
*/
public int remove(){
if(isEmpty())
throw new RuntimeException("队列为空,无法出队");
int temp = arr[front++];//将队头节点赋给临时变量,然后将队头指针后移
front %= size;//这里要取模,是为了当删除最后一个结点的时候,front 就会后移变成10,那么经过这一步之后,就会变成0
return temp;//返回队头节点的值
}
/**
* 判断循环队列是否为空
* @return
*/
public boolean isEmpty(){
return rear == front;
}
/**
* 判断循环队列是否为满
* @return
*/
public boolean isFull(){
return (rear + 1) % size == front;
}
}
广度优先搜索:
public class BreadSearch {
public int[][] addEdge;
public Node[] node;
public Queue queue;
public int size;
public int count;//表示节点的个数
public BreadSearch(int max){
size = max;
count = 0;
addEdge = new int[max][max];
node = new Node[max];
queue = new Queue(max);
}
/**
* 添加节点
* @param label
*/
public void addNode(char label){
if(count == size)
throw new RuntimeException("已满,无法继续插入节点");
node[count++] = new Node(label);
}
/**
* 表示start ---end这两个节点是相邻的关系
* @param start
* @param end
*/
public void addEdge(int start, int end){
addEdge[start][end] = 1;
addEdge[end][start] = 1;
}
/**
* 进行广度优先搜索
*/
public void bfs(){
node[0].wasVisited = true;//将第一个节点标记已经被访问了
queue.insert(0);//将第一个节点压入队列中
System.out.print(node[0].label +" ");
while(!queue.isEmpty()){
//当队列没有空的时候,那么就将队头节点从队列中跳出,查看是否含有相邻的而且没有被访问过的节点
//如果有,就将其压入队列中,否则再重复上面的操作,再次将队头节点从队列中跳出,再查看,直到队列为空
int current = queue.remove();//从队列中跳出队头节点
int temp = getUnvisitedNode(current);//查看是否含有相邻的而且没有被访问过的节点
while(temp != -1){
//temp不等于-1,表示含有相邻的而且没有被访问过的节点,将其压入队列中,同时将其标记已经被访问过了
node[temp].wasVisited = true;
queue.insert(temp);
System.out.print(node[temp].label +" ");
temp = getUnvisitedNode(current);
}
}
}
/**
* 获取第 i+1个节点是否含有相邻的而且没有被访问过的节点,如果有,就将这个节点返回,否则返回-1
* @param i
* @return
*/
public int getUnvisitedNode(int i){
for(int j = 0; j<size; j++){
if(addEdge[i][j] == 1 && node[j].wasVisited == false)
//如果含有相邻的而且没有被访问过的节点,就将这个节点返回
return j;
}
return -1;//没有相邻的节点或者虽然有相邻的节点但是已经被访问过了
}
}
测试广度优先搜索:
//测试广度优先搜索
public class BreadSearchApp {
public static void main(String[] args){
BreadSearch breadSearch = new BreadSearch(10);
breadSearch.addNode('A');
breadSearch.addNode('B');
breadSearch.addNode('C');
breadSearch.addNode('D');
breadSearch.addNode('E');
breadSearch.addNode('F');
breadSearch.addNode('G');
breadSearch.addNode('H');
breadSearch.addNode('I');
breadSearch.addNode('J');
breadSearch.addEdge(0,1);
breadSearch.addEdge(1,2);
breadSearch.addEdge(2,3);
breadSearch.addEdge(3,4);
breadSearch.addEdge(4,5);
breadSearch.addEdge(5,6);
breadSearch.addEdge(6,7);
breadSearch.addEdge(7,8);
breadSearch.addEdge(8,9);
breadSearch.bfs();
}
}
做得不好的地方,请见谅,哈哈😀。