深度优先搜索(DFS)以及相应的java实现代码

深度优先搜索(Depth First Search,DFS)

主要思想

深度优先搜索是从起点开始,按照某种权重规则,选择优先级最高的路径,从而到达下一个结点,而后在上一步选择的基础上进行下一步优先级最高的选择,反复进行类似探索。
最终会遇到两种情况:

  1. 就是找到了终点(目标),即结束搜索
  2. 碰到了“绝路”,也就是前面没有路了。此时“回头”选择离此次最近的另外一条路(相当于我开始是一个方向走到头,发现没有路了,则返回到最近的分叉路口处选择另一条路。),再继续上述探索,如果直至最后回到起点,发现我已经没有未走过的路了,即把所有的路都走了一遍还没有找到终点(目标),则没有搜索结果

例子

如何从以下的S点到达E点,只能进行上下左右的移动且每次只能移动一格,并且不能跨域障碍,同时需要达到一个不错的效果(路途要尽量短)。
例子
对于这个例子,前提准备数据类型与规则制定:
1.先从S出发,因为E在S的右下方,所以选择右,下移动的优先级大于左,上移动
2.其次还需要两个容器去装有已经查找过的结点,以及将要查找的结点。而我选择了用充当存放结点的容器,由于栈的数据结构特点是:后进行先出,刚好满足当探索碰到了“死路”的时候,会弹出最近的存入了将要探索的结点,即返回到最近分叉路口选择另一条路。
3.而对于结点:需要有坐标,同时要标记是否是阻碍结点,而且还需要记入父亲结点来记录路径

java代码实现(包含数据存储的栈,结点类)

import java.util.ArrayList;

public class Dfs {
    /**
     * dfs算法搜索:它是通过栈的数据结构来进行充分的进行搜索,先初始化开始节点与路(棋盘),
     * 起始点的父节点为NULL。它通过判断周围的节点是否被标记过或则是BLOCKED,
     * 将下一步探索,没被标记(已查)且不是BLOCKED转态的结点的压入将要搜索的(frontier)栈内,
     * 并将原结点压入已经检查过的栈内(explored)。不断对frontier栈顶元素进行判断是否到达目标位置
     * 如果未到,继续探索下一步可以走的位置,有则压入frontier中,没有则继续抛出frontier栈顶元素,
     * 直至frontier元素为0,则没有下一步可以走了,若此时还是没到目标位置,则不能到达目标位置
     */
    // 将要检查的结点
    Stack frontier;
    // 检查过的结点
    Stack explored;
    // 当前结点
    Node currentNode;
    // 表格(路况)的初始化
    RoadCondition roadCondition;
    /**
    *初始化两个栈和棋盘,同时将起始点压入frontier栈中
    */
    public Dfs(Node start) {
        frontier=new Stack();
        explored=new Stack();
        roadCondition=new RoadCondition();
        this.frontier.push(start);
    }

    /**
     * DFS算法
     * @param goal 目标结点
     * @return 如果找到目标结点则返回该结点,否则返回null
     */
    public Node myDfs(Node goal){
        // 如果有还可以进行检查(探索)元素,即还可以有下步走的地方
        while (frontier.size()!=0){
            // 弹出frontier栈顶元素进行匹配
            currentNode=frontier.pop();
            if(currentNode.equals(goal)){
                // 返回值当前结点
                return currentNode;
            }else{
                // 通过successor方法,将currentNode的下一步可以走的结点压入frontier栈中
                successor(currentNode);
                // 将当前结点设置为已检查的结点
                explored.push(currentNode);
            }
        }
        // 没找到,返回null
        return null;
    }

    /**
     * 将currentNode的下步可以走的结点压入frontier栈中
     * @param node 结点元素
     */
    public void successor(Node node){
        // 首先判断下一步有没有越界,同时也不是有阻碍的结点,以前也没有检查过。
        // 由于dfs算法是通过栈的数据结构存储,所以先进后出,故判断下一步的走法,
        // 先压入:上,左,下,右。所以优先就是刚好反过来的:右,下,左,上
        // 将符合条件的结点,压入将要探索的frontier栈中,并且记入父节点,便于画路径
        if(node.state.x-1>=0&&
        	!roadCondition.road[node.state.x-1][node.state.y].block&&
            !explored.contains(roadCondition.road[node.state.x-1[node.state.y])&&
            !frontier.contains(roadCondition.road[node.state.x-1][node.state.y]))
            {
            	// 将该结点压入frontier栈内,表示下一步可以探索的结点
            	frontier.push(roadCondition.road[node.state.x-1][node.state.y]);
            	// 记入下一步可以探索结点的父节点,便于回溯路径
            	roadCondition.road[node.state.x-1][node.state.y].parent=node;
        }
        // 判断向是否可以向左走
        if(node.state.y-1>=0&&
        	!roadCondition.road[node.state.x][node.state.y-1].block&&
            !explored.contains(roadCondition.road[node.state.x][node.state.y-1])&&
            !frontier.contains(roadCondition.road[node.state.x][node.state.y-1]))
            {
            	// 将该结点压入frontier栈内,表示下一步可以探索的结点
            	frontier.push(roadCondition.road[node.state.x][node.state.y-1]);
            	// 记入下一步可以探索结点的父节点,便于回溯路径
            	roadCondition.road[node.state.x][node.state.y-1].parent=node;
        }
        // 判断是否可以向下走
        if(node.state.x+1<=8&&
        	!roadCondition.road[node.state.x+1][node.state.y].block&&
            !explored.contains(roadCondition.road[node.state.x+1][node.state.y])&&
            !frontier.contains(roadCondition.road[node.state.x+1][node.state.y]))
            {
            	// 将该结点压入frontier栈内,表示下一步可以探索的结点
            	frontier.push(roadCondition.road[node.state.x+1][node.state.y]);
            	// 记入下一步可以探索结点的父节点,便于回溯路径
            	roadCondition.road[node.state.x+1][node.state.y].parent=node;
        }
        // 判断是否可以向右走
        if(node.state.y+1<=8&&
        	!roadCondition.road[node.state.x][node.state.y+1].block&&
            !explored.contains(roadCondition.road[node.state.x][node.state.y+1])&&
            !frontier.contains(roadCondition.road[node.state.x][node.state.y+1]))
            {
            	// 将该结点压入frontier栈内,表示下一步可以探索的结点
            	frontier.push(roadCondition.road[node.state.x][node.state.y+1]);
            	// 记入下一步可以探索结点的父节点,便于回溯路径
           		roadCondition.road[node.state.x][node.state.y+1].parent=node;
        }
    }
    /**
     * 如果可以到达目标位置,对通过的路径进行描绘
     * @param node 寻找到的目标结点
     */
    public void show(Node node){
        ArrayList<Node> path=new ArrayList<>();
        // 如果结点是null,则没有找到目标
        if(node==null){
            System.out.println("抱歉没有路径通往目的地!");
            return;
        }else {
        	// 记录通往目标结点所经过的所有的结点
            Node current_tag=node;
            while (current_tag!=null){
                path.add(current_tag);
                current_tag=current_tag.parent;
            }
            // 对棋盘(路况信息)以及通往目标结点路径的描绘
            for (Node[] nodes : roadCondition.road) {
                System.out.print("|");
                for (Node n : nodes) {
                    if(n.block){
                        System.out.print("X |");
                    }else if(path.contains(n)){
                        System.out.print("->|");
                    }else {
                        System.out.print("  |");
                    }
                }
                System.out.println();
            }
        }
    }
}


    /**
     *	数据结构:栈
     *(底层使用的是LinkedList存储元素)
     */
     
class Stack {
    LinkedList<Node> list;

    public Stack(LinkedList<Node> list) {
        this.list = list;
    }

    public Stack() {
        list=new LinkedList<>();
    }
	// 判断是否包含node结点
    public boolean contains(Node node){
        return list.contains(node);
    }
	// 压栈操作
    public void push(Node node){
        list.push(node);
    }
    // 弹出栈顶元素
    public Node pop(){
        return list.removeFirst();
    }
    // 返回栈里元素的个数
    public int size(){
        return list.size();
    }
    @Override
    public String toString() {
        return "Stack{" +
                "list=" + list +
                '}';
    }
}

    /**
     *	节点元素:Node
     */
     
class Node{
	// 坐标类
    MyState state;
    // 父类结点
    Node parent;
    // 是否是障碍结点
    boolean block;

    public Node() {
    }

    public Node(MyState state, Node parent, boolean block) {
        this.state = state;
        this.parent = parent;
        this.block = block;
    }

    @Override
    public String toString() {
        return "Node{" +
                "state=" + state +
                ", parent=" + parent +
                ", block=" + block +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Node node = (Node) o;
        return Objects.equals(state, node.state);
    }

    @Override
    public int hashCode() {
        return Objects.hash(state, parent, block);
    }
}

    /**
     * 下标类
     */

class MyState{
	// 横坐标
    int x;
    // 纵坐标
    int y;

    public MyState(int x, int y) {
        this.x = x;
        this.y = y;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        MyState myState = (MyState) o;
        return x == myState.x &&
                y == myState.y;
    }

    @Override
    public int hashCode() {
        return Objects.hash(x, y);
    }

    @Override
    public String toString() {
        return "MyState{" +
                "x=" + x +
                ", y=" + y +
                '}';
    }
}


    /**
     *棋盘(路况)类(包括9X9棋盘生成,障碍的生成)
     */
     
class RoadCondition {
    Node[][] road=new Node[9][9];

    public RoadCondition() {
        Random random=new Random();
        // 生成9X9的棋盘
        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 9; j++) {
                // 设置生成障碍的概率,这里是设置1/5
                int tmp=random.nextInt(5);
                road[i][j]=new Node(new MyState(i,j),null,tmp==1);
            }
        }
        
        // 手动设置,默认起点(0,0),终点(8,8)不是障碍结点
        road[0][0].block=false;
        road[8][8].block=false;
    }

    @Override
    public String toString() {
        return "RoadCondition{" +
                "road=" + Arrays.toString(road) +
                '}';
    }
    public void show(){
        for (Node[] nodes : road) {
            System.out.print("|");
            for (Node node : nodes) {
                if(node.block){
                    System.out.print("X |");
                }else {
                    System.out.print("  |");
                }
            }
            System.out.println();
        }
    }
}

运行结果展示

结果1

结果1

结果2

请添加图片描述

总结

注意:深度优先搜索算法找到的路径并不一定是最短(优)路径,这里就比与之对应的广度优先搜索稍微“弱”点。
而对于广度优先搜索来说所找到的路径是最短(优)路径。而广度优先搜索算法与深度优先搜索算法是一模一样的,唯一不一样的是广度优先搜索算法将栈换成了队列来存储结点元素!
谢谢观看,欢迎一起讨论!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值