使用DFS查找迷宫中的所有路径

时间:2017-10-18 19:00:16

标签: c algorithm data-structures depth-first-search maze

鉴于迷宫中的源和目标单元格,我想使用DFS找到所有可能的迷宫解决方案。 0表示我迷宫中的墙。

Visualise the maze

我已经成功编写了一个程序,但这只给了我一个路径。我已经想到了将它扩展到所有路径的所有可能性,但遗憾的是,即使经过数小时(确切地说是2天),我也无法找到解决方案。< / p>

我想要保持一个&#34;访问&#34; 每个节点的数组,这样当我回溯时,我不会为前一个节点执行相同的移动。但是,这样做只会产生一个完全不同的路径(如果有的话)没有相同的节点。

我还想到了其他一些方法,但即使这样也会产生一些问题。

我也检查了类似的问题,但没有一个是具体到我的上下文中。有一个使用显式递归的解决方案。但是,我想要的是使用堆栈并找到所有可能的路径。可悲的是,我找不到任何可信的东西(至少以我能理解的方式)。

这是一个路径的代码。

编辑:我现在已经实施了&#34;正确的&#34; DFS。我还添加了一个堆栈来跟踪当前路径。然后程序还会检查未探测的节点。但是,它以其他顺序检查,因此我仍然只能获得一条路径!

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

typedef struct Point
{
    int x,y;
}Point;

typedef struct stack
{
    struct Point pt;
    struct stack* next;
    void push(int, int);
    Point pop();
}stack, stack_path;

stack*front =NULL;
stack_path*front_path =NULL;


void push(int x, int y)
{
    if(front==NULL)
    {
        front = (stack*)malloc(sizeof(stack));
        front -> pt.x = x; front -> pt.y = y;
        front -> next = NULL;
        return;
    }
    stack* temp = (stack*)malloc(sizeof(stack));
    temp -> pt.x = x; temp -> pt.y = y;
    temp -> next = front;
    front = temp;
}

Point pop()
{
    if(front != NULL)
    {
        stack* temp = front;
        Point pt = front -> pt;
        front = front -> next;
        free(temp);
        return pt;  
    }
}

void push_path(int x, int y)
{
    if(front_path==NULL)
    {
        front_path = (stack*)malloc(sizeof(stack_path));
        front_path -> pt.x = x; front_path -> pt.y = y;
        front_path -> next = NULL;
        return;
    }
    stack_path* temp = (stack*)malloc(sizeof(stack_path));
    temp -> pt.x = x; temp -> pt.y = y;
    temp -> next = front_path;
    front_path = temp;
}

Point pop_path()
{
    if(front_path != NULL)
    {
        stack_path* temp = front_path;
        Point pt = front_path -> pt;
        front_path = front_path -> next;
        free(temp);
        return pt;  
    }
}

bool inMaze(Point pt)
{
    return (pt.x>=0 && pt.y>=0 && pt.x<5 && pt.y<6);
}


int main()
{

    struct Point pt1;

    int visited[30]={0};
    push(0,0);
    push_path(0,0);

    struct Point dest = {3,4};
    int maze[5][6] = {{1,0,1,1,1,1},
                      {1,0,1,0,1,1},
                      {1,1,1,0,1,1},
                      {0,0,0,0,1,0},
                      {1,1,1,0,1,1}};
    int paths[30]={0};
    int dx[4]={-1, 0, 0, 1};
    int dy[4]={0,-1, 1, 0};
    while(front!=NULL)
    {
        Point pt = pop();

        if(pt.x==dest.x && pt.y==dest.y)
        {
            push_path(pt.x,pt.y);
            int i;

            visited[6*pt.x+pt.y] = 0;
            stack_path *temp = front_path;
            while(temp!=NULL)
            {
                printf("%d%d ",temp->pt.x, temp->pt.y);
                temp = temp->next;
            }
            printf("\n");
            pop_path();

        }
        else
        {
            if(!visited[6*pt.x+pt.y])
            {
                visited[6*pt.x+pt.y] = 1;
                push_path(pt.x,pt.y);
            }
            int i;
            int flag =0;
            for(i=0; i<4; i++)
            {
                pt1.x = dx[i] + pt.x;
                pt1.y = dy[i] + pt.y;
                if(inMaze(pt1) && maze[pt1.x][pt1.y]==1 && visited[6*pt1.x+ pt1.y]==0) 
                {
                    push(pt1.x,pt1.y);
                    flag = 1;
                }
            }
            if(flag==0)
                pop_path();
        }
    }
    return 0;
}

有人可以指导我修改此代码,以便找到所有路径。期待社区的帮助!

PS:因此目前不允许我嵌入图片,因此它会自动创建一个链接。

PS:此问题已被标记为与其他一些问题相关的重复。为了您的信息,我在发布问题之前已经完成了这个问题!如果有人会在那里阅读答案,你可以看到它不是&#34;接受&#34; !它只是说你需要做&#34;所有&#34; 排列!如果只有一个人会打扰通过另一个答案(在另一个问题中),他们会意识到仅应用来向北,东北或东方方向移动!此外,我还明确表示我想要一个递归解决方案 - 这是另一个问题所使用的内容!

编辑2 工作解决方案

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

typedef struct Point
{
    int x,y;
}Point;

typedef struct stack
{
    struct Point pt;
    struct stack* next;
    int dir_count;
}stack;

stack*front =NULL;



void push(int x, int y)
{

    stack* temp = (stack*)malloc(sizeof(stack));
    temp -> pt.x = x; temp -> pt.y = y;
    temp -> next = front;
    front = temp;
}

Point pop()
{
    if(front != NULL)
    {
        stack* temp = front;
        Point pt = front -> pt;
        front = front -> next;
        free(temp);
        return pt;  
    }
}

bool inMaze(Point pt)
{
    return (pt.x>=0 && pt.y>=0 && pt.x<5 && pt.y<6);
}


int main()
{

    struct Point pt1,pt2;
    struct Point pt = {0,0};

    push(0,0);
    front->dir_count = 0;

    struct Point dest = {3,4};
    int maze[5][6] = {{1,0,1,1,1,1},{1,0,1,0,1,1},{1,1,1,0,1,1},{0,0,0,0,1,0},{1,1,1,0,1,1}};

    int dx[4]={-1, 0, 0, 1};
    int dy[4]={0,-1, 1, 0};

    int flag_pop = 0;
    while(front != NULL)
    {
        if(front->pt.x==dest.x && front->pt.y==dest.y)
        {

            stack* temp = front;
            while(temp != NULL)
            {
                printf("%d%d ", temp->pt.x, temp->pt.y);
                temp = temp->next;
            }
            printf("\n");
            pt = pop();

        }
        else
        {
            int i,k;
            int flag_push =0, count = 0, moves=0;
            for(k=0;k<4;k++)
            {
                pt2.x = dx[k] + front->pt.x;
                pt2.y = dy[k] + front->pt.y;
                if(maze[pt2.x][pt2.y]==0 || !inMaze(pt2) || !(pt.x != pt2.x || pt.y != pt2.y))
                    count++;
            }
            // count of possible moves for each node
            moves = 4-count;
            for(i=0; i<4; i++)
            {
                int flag=0;
                pt1.x = dx[i] + front->pt.x;
                pt1.y = dy[i] + front->pt.y;

                // if moves are exhausted
                if(!(front->dir_count<moves))
                    break;

                if(inMaze(pt1) && maze[pt1.x][pt1.y]==1 && (pt.x != pt1.x || pt.y != pt1.y) )
                {
                    stack* temp = front;
                    while(temp != NULL)
                    {
                        if(temp->pt.x == pt1.x && temp->pt.y == pt1.y)
                        {
                            flag = 1;
                            break;
                        }
                        temp = temp->next;
                    }

                    // if node is not there in the path
                    if(flag==0)
                    {
                        front->dir_count++;
                        push(pt1.x, pt1.y);
                        front -> dir_count = 0;
                        flag_push = 1;
                        break;
                    }   
                }
            }
            // if no move was done
            if(flag_push==0)
            {
                pt = pop();

            }
        }   

    }
    return 0;
}

2 个答案:

答案 0 :(得分:1)

我认为您需要清除访问中的相应字段,从堆栈中删除该点。

修改:另一个问题是,当您达到目标时需要回溯。在您的堆栈上,可能并不明显未开发的替代方案是什么以及当前路径是什么(您可能能够使用被访问的数组来跟踪它,但它似乎比使用递归或添加相应信息的“仅仅”更复杂。你的Stack stuct)

此外,后续节点在实际调查时应标记为已访问,而不是在将它们推入堆栈时。

一些评论

  • 我认为代码使用递归而不是显式堆栈更具可读性

  • 你真的不需要访问,你可以暂时将路径上的迷宫字段更改为1(可能不会在迷宫应该是不可变的“真实”代码中执行此操作)。否则,我只是以与迷宫相同的方式构建它。

  • 我会改变推动,以便采取对称的方式。

  • 避免冗余膨胀代码。例如,front == NULL中的push分支在默认情况下是多余的 - 默认情况下在NULL情况下会完全相同。

编辑2

如果你真的想避免递归,我会将数据结构更改为:

typedef struct PathElement {
  int x;
  int y;
  int direction_to_next;
  struct PathElement* next;
  struct PathElement* prev;
} path;

这将允许您轻松地向任何方向前进(例如,当您想要打印时,您将位于路径的末尾)。当你回溯到PathElement时,增加direction_to_next并继续那里直到你已经用完所有4个可能的方向。不要提前“推”替代品。

答案 1 :(得分:0)

  • 对于搜索中的每个点,请保留对前一点的引用。操作后,始终将物品从堆叠中弹出。 请注意,如果没有解决方案,您的代码将无限循环。当你走到最后,你可以使用前辈来回溯并找到完整的路径。

  • 摆脱被访问的数组,并防止在DFS期间出现循环,检查以确保添加到路径中的点当前不在路径中。由于迷宫中只有30个可能的点,因此可以使用位域来表示给定点是否在路径中。

  • 此外,不要过早地突破循环(在找到第一个可用的移动时)。