目录
题目 1:
题目 2:
一、思路
二、代码流程
一、文件准备
二、创建全局变量的栈
三、有效性检验
四、路径查找
五、栈的逆序及路径输出
六、题目1 main函数
三、进阶难度
一、全局变量的创建
二、有效性检验
三、栈的深拷贝
四、最优路径的搜索
五、路线输出
六、题目2 main函数
四、总结
链接:迷宫问题__牛客网
来源:牛客网
定义一个二维数组 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],既第一格是可以走的路。
数据范围: 2≤n,m≤10 2 \le n,m \le 10 \ 2≤n,m≤10 , 输入的内容只包含 0≤val≤1 0 \le val \le 1 \ 0≤val≤1
链接:地下迷宫_滴滴笔试题_牛客网
来源:牛客网
小青蛙有一天不小心落入了一个地下迷宫,小青蛙希望用自己仅剩的体力值P跳出这个地下迷宫。为了让问题简单,假设这是一个n*m的格子迷宫,迷宫每个位置为0或者1,0代表这个位置有障碍物,小青蛙达到不了这个位置;1代表小青蛙可以达到的位置。小青蛙初始在(0,0)位置,地下迷宫的出口在(0,m-1)(保证这两个位置都是1,并且保证一定有起点到终点可达的路径),小青蛙在迷宫中水平移动一个单位距离需要消耗1点体力值,向上爬一个单位距离需要消耗3个单位的体力值,向下移动不消耗体力值,当小青蛙的体力值等于0的时候还没有到达出口,小青蛙将无法逃离迷宫。现在需要你帮助小青蛙计算出能否用仅剩的体力值跳出迷宫(即达到(0,m-1)位置)。
如果能逃离迷宫,则输出一行体力消耗最小的路径,输出格式见样例所示;如果不能逃离迷宫,则输出"Can not escape!"。 测试数据保证答案唯一
首先创建一个二维数组接收迷宫,对于题目一,以(0,0)点为起始位置,分别对四个方向的位置进行有效性判断,有效就移动至该点并重复此操作,这里用栈的数据结构存储点,将走过的路径存储在栈中,若路线错误,则回退,并将栈中数据弹出,在移动过程中,将走过的迷宫,即迷宫数组值为0的赋一个标记值,在回退过程中也进行有效性判断,只有值为0的点可以去移动。最终找到路径,将栈逆序,打印即为路径。
由于使用的c语言,无栈的库函数,需要自己写。可以看看线性表综合讲述。
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
#include
//创建一个点的数据类型
typedef struct Coordinate
{
int row;
int col;
}Coor;
/
typedef Coor STD;
typedef struct Stack
{
STD* base;
int top;
int capacity;
}S;
void StackInit(S* ps)
{
assert(ps);
ps->base = (STD*)malloc(sizeof(STD) * 4);
if (ps->base == NULL)
{
printf("malloc fail\n");
exit(-1);
}
ps->capacity = 4;
ps->top = 0;
}
void StackDestory(S* ps)
{
assert(ps);
free(ps->base);
ps->base = NULL;
ps->top = ps->capacity = 0;
}
// 入栈
void StackPush(S* ps, STD x)
{
assert(ps);
// 满了-》增容
if (ps->top == ps->capacity)
{
STD* tmp = (STD*)realloc(ps->base, ps->capacity * 2 * sizeof(STD));
if (tmp == NULL)
{
printf("realloc fail\n");
exit(-1);
}
else
{
ps->base = tmp;
ps->capacity *= 2;
}
}
ps->base[ps->top] = x;
ps->top++;
}
// 出栈
void StackPop(S* ps)
{
assert(ps);
// 栈空了,调用Pop,直接中止程序报错
assert(ps->top > 0);
//ps->a[ps->top - 1] = 0;
ps->top--;
}
STD StackTop(S* ps)
{
assert(ps);
// 栈空了,调用Top,直接中止程序报错
assert(ps->top > 0);
return ps->base[ps->top - 1];
}
int StackSize(S* ps)
{
assert(ps);
return ps->top;
}
bool StackEmpty(S* ps)
{
assert(ps);
return ps->top == 0;
}
/
为了减少函数复杂程度,减少传参数,以及方便调用,将栈设为全局变量
S Path;
要防止数组越界及所走通路只能为存0的点
bool checkCoor(int**maze, int N, int M, Coor coor)
{
if ((coor.row >= 0 && coor.col < N)
&& (coor.row >= 0 && coor.col < M)
&& maze[coor.row][coor.col] == 0)
return true;
else
return false;
}
我们先将点存入栈中,让后分四个方向进行判断,如果有方向可走则递归此函数,重复上述步骤,反之,弹出入栈的点进行回退。
bool SearchmazePath(int**maze, int N, int M, Coor coor)
{
//点入栈
StackPush(&Path, coor);
if (coor.row == N - 1 && coor.col == M - 1)//找到目标位置返回真
return true;
Coor next;
maze[coor.row][coor.col] = 2;//将走过的点标记为2
//分别对上、下、左、右四个方向进行判断,递归调用。
next = coor;
next.col += 1;
if (checkCoor(maze, N, M, next))
{
if (SearchmazePath(maze, N, M, next))
return true;
}
next = coor;
next.col -= 1;
if (checkCoor(maze, N, M, next))
{
if (SearchmazePath(maze, N, M, next))
return true;
}
next = coor;
next.row -= 1;
if (checkCoor(maze, N, M, next))
{
if (SearchmazePath(maze, N, M, next))
return true;
}
next = coor;
next.row += 1;
if (checkCoor(maze, N, M, next))
{
if (SearchmazePath(maze, N, M, next))
return true;
}
//为到达目标点,弹出数据,递归回退。
StackPop(&Path);
return false;
}
因为栈是先入后出,所以想要将移动路径打印出来就需要逆序栈。
void PrintPath(S*path)
{
S rpath;//在创建一个栈
StackInit(&rpath);
while (!StackEmpty(path))//将原栈的数据存入逆序栈中
{
StackPush(&rpath, StackTop(path));
StackPop(path);
}
while (!StackEmpty(&rpath))
{
Coor c = StackTop(&rpath);
printf("(%d,%d)\n", c.row, c.col);//进行打印
StackPop(&rpath);
}
StackDestory(&rpath);
}
int main()
{
int N, M;
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]);
}
StackInit(&Path);
Coor start = { 0,0 };
if (SearchmazePath(maze, N, M, start))
{
//printf("找到出口了,路径如下:\n");
PrintPath(&Path);
}
/*else
printf("没有找到出口\n");*/
StackDestory(&Path);
for (int i = 0; i < N; i++)
free(maze[i]);
free(maze);
maze = NULL;
}
return 0;
}
对于题目二,增加了体力及最优路线的限制,总体思路与1一致,为此增加一个栈的全局变量,用于存储最优路线,路线节点越少越好,则可以通过比较栈节点的大小来选出最优解。
S path;
S minpath;
通路标记发生了改变
bool checkCoor(int** maze, int N, int M, Coor pos)
{
if (pos.row >= 0 && pos.row < N
&& pos.col >= 0 && pos.col < M
&& maze[pos.row][pos.col] == 1)//1可通行
return true;
else
return false;
}
当找到一个更优路线后,需要将path赋给minpath,能直接使用=号吗,不能。一、path和minpath都是指针,将path副给minpath,是让其指向path所指的同一块内存空间,会导致minpath原来所指空间的内存泄露,以及后续对path所指空间的重复释放。所以需要深拷贝。
void StackCopy(S* ppath, S* pcopy)
{
pcopy->base = (STD*)malloc(sizeof(STD*)*ppath->capacity);//开辟一块新空间
memcpy(pcopy->base, ppath->base, sizeof(STD)*ppath->top);//将path的数据拷贝给minpath
pcopy->top = ppath->top;
pcopy->capacity = ppath->capacity;
}
因为存在多组路径,所以不在用bool返回值仅找出一组解。
void SearchmazePath(int** maze, int N, int M, Coor cur, int P)
//新增体力参数
{
StackPush(&path, cur);
if (cur.row == 0 && cur.col == M - 1)//目标位置变更
{
// 找到了更短的路径,更新minpath;
if (P >= 0 && StackEmpty(&minpath)
|| StackSize(&path) < StackSize(&minpath))
{
StackDestory(&minpath);
StackCopy(&path, &minpath);
}
}
// 探测cur位置得上下左右四个方向
Coor next;
maze[cur.row][cur.col] = 2;
next = cur;
next.row -= 1;
if (checkCoor(maze, N, M, next))
SearchmazePath(maze, N, M, next, P - 3);
next = cur;
next.row += 1;
if (checkCoor(maze, N, M, next))
SearchmazePath(maze, N, M, next, P);
next = cur;
next.col -= 1;
if (checkCoor(maze, N, M, next))
SearchmazePath(maze, N, M, next, P - 1);
next = cur;
next.col += 1;
if (checkCoor(maze, N, M, next))
SearchmazePath(maze, N, M, next, P - 1);
// 因为不同解路线之间可能存在交叉,需要恢复原路线。
maze[cur.row][cur.col] = 1;
StackPop(&path);
}
题目2的输出与一有些不同,每个节点用“,”间隔,最后一个不加。
void PirntPath(S* ps)
{
// path数据倒到rPath
S rPath;
StackInit(&rPath);
while (!StackEmpty(ps))
{
StackPush(&rPath, StackTop(ps));
StackPop(ps);
}
while (StackSize(&rPath) > 1)//还剩最后一个数据时跳出循环,做单独处理
{
Coor top = StackTop(&rPath);
printf("[%d,%d],", top.row, top.col);
StackPop(&rPath);
}
Coor top = StackTop(&rPath);
printf("[%d,%d]", top.row, top.col);
StackPop(&rPath);
StackDestory(&rPath);
}
int main()
{
int N = 0, M = 0, P = 0;
while (scanf("%d%d%d", &N, &M, &P) != 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]);
}
StackInit(&path);
StackInit(&minpath);
Coor start = { 0, 0 };
SearchmazePath(maze, N, M, start, P);
if (!StackEmpty(&minpath))
{
//printf("找到出口了,路径如下:\n");
PirntPath(&minpath);
}
else
printf("Can not escape!\n");
StackDestory(&path);
StackDestory(&minpath);
for (int i = 0; i < N; ++i)
free(maze[i]);
free(maze);
maze = NULL;
}
return 0;
}
这便是对数据结构的应用,在遇到不同问题时,选用合适的数据结构来实现目的。