图的搜索(深度优先搜索、广义优先搜索)

图的深度优先搜索

深度优先搜索算法(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();
    }
}

做得不好的地方,请见谅,哈哈😀。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值