迷宫问题(回溯算法)

 回溯算法

     回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。许多复杂的,规模较大的问题都可以使用回溯法,有“通用解题方法”的美称。(深度优先遍历,能走则走,不能走则退) 

牛客网链接 

迷宫问题__牛客网 (nowcoder.com)https://www.nowcoder.com/questionTerminal/cf24906056f4488c9ddb132f317e03bc

题目 

定义一个二维数组 N*M ,如 5 × 5 数组下所示:

int maze[5][5] = {
0, 1, 0, 0, 0,
0, 1, 1, 1, 0,
0, 0, 0, 0, 0,
0, 1, 1, 1, 0,
0, 0, 0, 1, 0,
}; 

要求

    它表示一个迷宫,其中的1表示墙壁,0表示可以走的路,只能横着走或竖着走,不能斜着走,要求编程序找出从左上角到右下角的路线。入口点为[0,0], 既第一格是可以走的路。

输出

(0,0)
(1,0)
(2,0)
(2,1)
(2,2)
(2,3)
(2,4)
(3,4)
(4,4)

题目分析 

     首先0为通路,1为墙。深度优先遍历,在迷宫里有上下左右四个方向。按照一定的顺序走(例:先走上,再走下,再走左,最后走右)。因为0为通路,先走下的话,把下又当作起点,那么它的上边还是0,继续走的话就又回去了,所以把它走过的路做个标记,就可以不用下去又折回去了。

     按照一定的顺序去走,难免会走到死胡同,即后边有标记,剩下三个方向都是墙,那么这条路肯定是走不通的,所以就原路返回,采用递归的方法,即回溯。返回到有其他通路的坐标,就继续往下走,如果再走到死胡同,就又返回,找新通路。这样最终一定会找出一条到终点通路。

     这里要把坐标输出, 这里考虑用栈的数据结构存储,先走的先进,后走的后进。当遇到死胡同时,就往回退,这个时候就把退的坐标出栈。那么以此下去,栈里存的肯定是最终通路的坐标。

     这里要求把坐标从入口,打印到出口,但是栈里后进的是出口坐标,所以就需要再借助一个栈来倒一下数据,再输出数据。

迷宫问题(回溯算法)_第1张图片

代码实现 

#include 
#include 
#include 
typedef struct pointer
{
    int row;
    int col;
}pointer;

typedef pointer StaDateType;

typedef struct stack
{
    StaDateType* arr;
    int top;
    int capicity;
}Stack;

void StactInit(Stack* ps);
void StackPush(Stack* ps, StaDateType date);
void StackPop(Stack* ps);
int StackSize(Stack* ps);
bool StackEmpty(Stack* ps);
void StackDestroy(Stack* ps);
StaDateType StackTop(Stack* ps);

void StactInit(Stack* ps)
{
    assert(ps);
    ps->arr = (StaDateType*)malloc(4 * sizeof(StaDateType));
    if (ps->arr != NULL)
    {
        ps->capicity = 4;
        ps->top = 0;
    }
}
void StackPush(Stack* ps, StaDateType date)
{
    assert(ps);
    if (ps->top == ps->capicity)
    {
        StaDateType* pa = (StaDateType*)realloc(ps->arr, ps->capicity * 2 * sizeof(StaDateType));
        if (pa != NULL)
        {
            ps->arr = pa;
            ps->capicity *= 2;
        }
    }
    ps->arr[ps->top] = date;
    ps->top++;
}
void StackPop(Stack* ps)
{
    assert(ps);
    assert(ps->top > 0);
    ps->top--;
}
StaDateType StackTop(Stack* ps)
{
    assert(ps);
    assert(ps->top > 0);
    return ps->arr[ps->top - 1];
}
int StackSize(Stack* ps)
{
    assert(ps);
    return ps->top;
}
bool StackEmpty(Stack* ps)
{
    assert(ps);
    return ps->top == 0;

}
void StackDestroy(Stack* ps)
{
    assert(ps);
    free(ps->arr);
    ps->top = 0;
    ps->arr = NULL;
}
bool IsPass(int** maze, int N, int M, pointer pos)
{
    if (pos.row >= 0 && pos.row < N &&
        pos.col >= 0 && pos.col < M &&
        maze[pos.row][pos.col] == 0)
    {
        return true;
    }
    else
    {
        return false;
    }
}
Stack path;//全局变量的path

void PrintPath()
{
    //倒数据
    Stack rpath;
    StactInit(&rpath);
    while (!StackEmpty(&path))
    {
        StackPush(&rpath, StackTop(&path));
        StackPop(&path);
    }
    //打印数据
    while (!StackEmpty(&rpath))
    {
        pointer top = StackTop(&rpath);
        printf("(%d,%d)\n", top.row, top.col);
        StackPop(&rpath);
    }

    StackDestroy(&rpath);
}
bool GetMazePath(int** maze, int N, int M, pointer cur)
{

    StackPush(&path, cur);
    //找到出口
    if (cur.row == N - 1 && cur.col == M - 1)
    {
        return true;
    }

    pointer next;
    maze[cur.row][cur.col] = 2;//标记走过的路
    //上
    next = cur;
    next.row -= 1;
    if (IsPass(maze, N, M, next))
    {
        if (GetMazePath(maze, N, M, next))
        {
            return true;//一但找到终点通路就不在找别的路径
        }
    }
    //下
    next = cur;
    next.row += 1;
    if (IsPass(maze, N, M, next))
    {
        if (GetMazePath(maze, N, M, next))
        {
            return true;
        }
    }
    //左
    next = cur;
    next.col -= 1;
    if (IsPass(maze, N, M, next))
    {
        if (GetMazePath(maze, N, M, next))
        {
            return true;
        }
    }
    //右
    next = cur;
    next.col += 1;
    if (IsPass(maze, N, M, next))
    {
        if (GetMazePath(maze, N, M, next))
        {
            return true;
        }
    }
    //死胡同
    StackPop(&path);
    return false;
  
}
int main()
{
    int N = 0;
    int M = 0;
    //开辟二维数组
    while (scanf("%d%d", &N, &M) != EOF)
    {
        int** maze = (int**)malloc(sizeof(int*) * N);
        for (int i = 0; i < N; i++)
        {
            maze[i] = (int*)malloc(sizeof(int) * M);
        }
        //输入二维数组数据
        for (int i = 0; i < N; i++)
        {
            for (int j = 0; j < M; j++)
            {
                scanf("%d", &maze[i][j]);
            }
        }
        StactInit(&path);
        //迷宫入口
        pointer entry = { 0,0 };
        if (GetMazePath(maze, N, M, entry))
        {
            PrintPath();//打印数据
        }
        else
        {
            printf("没有到终点的通路");
        }
        StackDestroy(&path);
        //释放二维数组
        for (int i = 0; i < N; i++)
        {
            free(maze[i]);
        }
        free(maze);
        maze = NULL;
    }
    return 0;
}

小结 

     回溯算法的核心思想就就是递归时往回返的一个过程,那么对递归的理解就相当重要,可以尝试画一下递归的展开图,对于理解递归的过程会有很大帮助。

你可能感兴趣的:(c语言,算法,深度优先)