BFS和DFS算法之间有什么区别?

时间:2019-04-05 05:12:40

标签: algorithm breadth-first-search dfs

使用BFS解决算法问题时发生超时。但是,存在DFS可以解决的问题。为什么会出现这种差异?

问题是要通过水平,垂直或对角移动来计算从(1,1)到(N,N)的到达次数。

BFS解决问题(最坏的情况)花费了1331.0ms,而DFS解决了62.0ms。 (我已经创建并测试了16 * 16数组。)

附加问题网址。 (但请理解这是韩文。) URL> https://www.acmicpc.net/problem/17070

我想听到的答案是...

  1. 我以为BFS算法会更快,但是为什么DFS会更快?
  2. BFS是否较慢,因为队列中有很多元素?我想知道确切原因。

实施代码>

课程位置{

int x;
int y;
int dir;

public Location(int x, int y, int dir) {
    super();
    this.x = x;
    this.y = y;
    this.dir = dir;
}

}

公共类主要{

static int[][] map;
static int Answer;
static int N;

public static void main(String args[]) throws IOException {

    BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    StringTokenizer st;

    N = Integer.parseInt(br.readLine());

    map = new int[N + 1][N + 1];
    for (int i = 1; i <= N; i++) {

        st = new StringTokenizer(br.readLine());

        for (int j = 1; j <= N; j++)
            map[i][j] = Integer.parseInt(st.nextToken());
    }
    DFS(1, 2, 0);
    System.out.println(Answer);
    Answer = 0;
    BFS(1, 2, 0);
    System.out.println(Answer);
    br.close();
}

static void DFS(int x, int y, int pre) {

    if (x == N && y == N) {

        Answer++;
        return;
    }

    if (pre == 0) {

        if (y + 1 <= N && map[x][y + 1] == 0)
            DFS(x, y + 1, 0);

        if (y + 1 <= N && map[x][y + 1] == 0 && x + 1 <= N && map[x + 1][y] == 0 && map[x + 1][y + 1] == 0)
            DFS(x + 1, y + 1, 1);
    } else if (pre == 1) {

        if (y + 1 <= N && map[x][y + 1] == 0)
            DFS(x, y + 1, 0);

        if (x + 1 <= N && map[x + 1][y] == 0)
            DFS(x + 1, y, 2);

        if (y + 1 <= N && map[x][y + 1] == 0 && x + 1 <= N && map[x + 1][y] == 0 && map[x + 1][y + 1] == 0)
            DFS(x + 1, y + 1, 1);
    } else {

        if (x + 1 <= N && map[x + 1][y] == 0)
            DFS(x + 1, y, 2);

        if (y + 1 <= N && map[x][y + 1] == 0 && x + 1 <= N && map[x + 1][y] == 0 && map[x + 1][y + 1] == 0)
            DFS(x + 1, y + 1, 1);
    }
}

static void BFS(int startX, int startY, int dir) {

    ArrayDeque<Location> arrayDeque = new ArrayDeque<>();
    arrayDeque.add(new Location(startX, startY, dir));

    Location location;
    int x, y, pre;

    while(!arrayDeque.isEmpty()) {

        location = arrayDeque.remove();
        x = location.x;
        y = location.y;
        pre = location.dir;

            if(x == N-1 && y == N-1) {
               Answer++; continue;
            }

        if (pre == 0) {

            if (y + 1 <= N && map[x][y + 1] == 0)
                arrayDeque.add(new Location(x, y + 1, 0));

            if (y + 1 <= N && map[x][y + 1] == 0 && x + 1 <= N && map[x + 1][y] == 0 && map[x + 1][y + 1] == 0)
                arrayDeque.add(new Location(x + 1, y + 1, 1));
        } else if (pre == 1) {

            if (y + 1 <= N && map[x][y + 1] == 0)
                arrayDeque.add(new Location(x, y + 1, 0));

            if (x + 1 <= N && map[x + 1][y] == 0)
                arrayDeque.add(new Location(x + 1, y, 2));

            if (y + 1 <= N && map[x][y + 1] == 0 && x + 1 <= N && map[x + 1][y] == 0 && map[x + 1][y + 1] == 0)
                arrayDeque.add(new Location(x + 1, y + 1, 1));
        } else {

            if (x + 1 <= N && map[x + 1][y] == 0)
                arrayDeque.add(new Location(x + 1, y, 2));

            if (y + 1 <= N && map[x][y + 1] == 0 && x + 1 <= N && map[x + 1][y] == 0 && map[x + 1][y + 1] == 0)
                arrayDeque.add(new Location(x + 1, y + 1, 1));
        }
    }
}

}

2 个答案:

答案 0 :(得分:4)

BFS和DFS都具有O(|V| + |E|)的时间复杂性,并且您遇到的时间差很可能是由于BFS的实现中的一个错误而引起的,该错误打破了循环不变

在实施BFS时犯下的最常见错误之一是多次将同一元素添加到队列中。一个顶点v只能添加一次到队列中,这样就可以确保一次将其删除。除非您这样做,否则渐近运行时(即它的复杂性)将不再是线性的。您可以在相关的CLRS章节中找到证明,这是基于他们引入的循环不变概念。

换句话说,BFS是遍历算法。它找出哪些个顶点是可到达的,而不是到达每个顶点v的路由数量。如果您尝试计算从Kv到BFS到达每个v的方法(1, 1),则复杂度将大于线性。 如果问题要求您找到Kv,那么您的方法应该是使用备注和动态编程,而不是BFS。

具体来说,根据您提供的代码,您的算法似乎无法跟踪先前是否探索过顶点(即网格中的单元)。这将导致顶点被多次浏览,这超越了使用诸如BFS和DFS之类的图遍历算法的要点。使用我上面提到的术语,您将遇到BFS的循环不变式,后者指出每个顶点仅从队列中弹出一次。这会导致代码的复杂性远高于线性。

您应该研究术语“记忆化” ,并找出一种方法来找到(N, N)的解决方案,使用您为{{ 1}},(N-1, N-1)(N-1, N)

答案 1 :(得分:0)

您的BFS实现使用动态内存分配和ArrayDeque;您的DFS可以避免这种情况。这会增加BFS的每个节点的成本,尽管奇怪的是这么多。

您可以尝试在每次DFS调用中分配一个新的Location(并可能从ArrayDeque中添加和删除它),看看是否会造成相同的性能损失。

另外,当x = y = N时,您的BFS不会直接停止;但我看不到它会大大增加运行时间。