白居易说,行路难,不在水,不在山,只在人情,反复间,世间
最不可直视的东西,一是太阳,二是人心。人心,大多难猜,真情总是难遇,
很多时候,我们总是自以为自己在对方心目中很重要。可期望越多,失望就越大。
有人说很多人,突然就不再联系了,和重不重要无关,却和需不需要有人。
每个人都有自己的生活,千万别高估自己在别人心目中的地位。
你以为你是别人心中的唯一,说不定,你就是可有可无的人,人心不是石头。
你对别人怎样,别人心里清楚,感情这座天枰,有来有往,才能维持不变,
愿你即像一个勇士,披荆斩棘,又能有人为你遮风挡雨,不需要刻意讨人喜欢
,只要无愧于心,做好自己。余生,别读感情,别猜人心,学会放过,
是善待自己的最好方式。
—————— 一禅心灵庙语
有关的题目链接: 🔜🔜🔜 牛客网:迷宫问题
首先我们从题目中知道了,迷宫中只有一个出口,出口的位置是在(右下角)
,开始位置是 (左上角)
,我们需要通
算法,找到出口,并把从 入口 ——> 出口
是路线通过坐标打印出来。
这里我们使用的是 C语言 代码解决的。0
为通路,1
为障碍物,不可走。
我们需要遍历整个迷宫,从而找到出口。
我们需要将,我们已经递归遍历走过的,坐标位置,标记成 2
,防止,往回走过的路,形成(往前走,再往回走,往前走,再往回走)的死循环。如下图:
这里我们使用递归的方式解决:而从题目可知,我们需要将从 入口 ——> 出口
是路线通过坐标打印出来。所以我们需要将坐标保存起来,这里我们使用 栈 将,坐标保存起来。因为 C语言 没有,有关栈的库,所以我们只能自己实现一个栈了。大家可以移动到 🔜🔜🔜( 图文并茂 ,栈的实现 —— C语言_ChinaRainbowSea的博客-CSDN博客 可以将栈的实现的代码,复制粘贴过来,是可以直接使用的。
定义一个全局栈: 用于存放我们从 入口 ——> 出口
的路线的坐标。
#include
#include // 断言
#include
#include // bool 布尔值
// 定义坐标结构体
typedef struct Postion
{
int x; // 数组坐标行
int y; // 数组坐标列
}PT;
#define OCE 2 // 扩容的倍数
typedef PT STDatatype; // 定义该栈中的数据类型,定义为 PT坐标结构类型
/*
* 创建栈的结构体
*/
typedef struct Stack
{
STDatatype* data; //动态顺序表开辟空间
int top; // 栈顶的标识
int copacity; // 栈容量
}ST;
ST path; // 全局栈
extern void StackInit(ST* st); // 初始化栈
extern void StackDestory(ST* st); // 销毁栈
extern void StackPush(ST* st, STDatatype x); // 栈顶入栈操作
extern void StackPop(ST* st); // 栈顶出栈操作
extern STDatatype StackTop(ST* st); // 取栈顶元素
extern int StackSize(ST* st); // 求栈中存在的数据个数
extern bool StackEmpty(ST* st); // 判断栈是否为空
/*
* 初始化栈
*/
void StackInit(ST* st)
{
assert(st); // 断言,判断st 不为 NULL;
st->data = (STDatatype*)malloc(sizeof(STDatatype) * OCE);
// 判断动态内存(堆上)开辟的空间是否成功
if (NULL == st->data)
{
perror("st->data 动态开辟空间");
exit(-1); // 非正常中止程序
}
st->copacity = 2; // 当前栈的容量
st->top = 0; // 栈顶位置
/*
* top = 0 ,表示的是栈顶的位置是该最后元素的下一个元素的位置
* top = 1; 表示的是栈顶的位置就是该最后元素的位置
*/
}
/*
* 销毁栈
*/
void StackDestory(ST* st)
{
assert(st); // 断言,st是否为空
free(st->data); // 释放该内存空间
st->data = NULL; // 手动置为NULL,防止非法访问
st->copacity = 0;
st->top = 0;
}
// 栈顶入栈操作
void StackPush(ST* st, STDatatype x)
{
assert(st); // 断言判断,st是否为NULL
// 判断栈是否满了,满了扩容
if (st->copacity == st->top)
{
// 动态(堆区)扩容开辟空间
STDatatype* tmp = (STDatatype*)realloc(st->data, st->copacity * OCE * sizeof(STDatatype));
// 判断动态内存开辟空间是否成功
if (NULL == tmp)
{
perror("tmp 扩容"); // 打印错误报告
exit(-1); // 非正常退出
}
else
{
// 动态开辟空间成功,转移主权
st->data = tmp;
st->copacity *= OCE; // 栈容量扩张
}
}
st->data[st->top] = x; // 栈顶入栈数值
st->top++; // 栈顶加加
}
/*
* 栈顶出栈操作
*/
void StackPop(ST* st)
{
assert(st); // 断言判断,st 是否为 NULL
assert(st->top > 0); // 出栈的位置必须大于 0 ,不能一直删除没了吧
st->top--; // 栈顶位置减减一下,就好了
/*
* 注意不可以把 st->data[st->top] = 0; 置为 0 的方式
* 因为可能你存放的数值就是 0 呢,不就有误导了吗
*/
}
/*
* 取出栈顶元素
*/
STDatatype StackTop(ST* st)
{
assert(st); // 断言判断,st 是否为NULL
assert(st->top > 0); // 栈不为空
return st->data[st->top - 1];
/*
* -1 ,是因为这里的top 初始化是 0
* 其top所指向的位置是 该最后一个元素的下一个位置
*/
}
/*
* 求出栈顶元素的个数
*/
int StackSize(ST* st)
{
assert(st); // 断言,判断st 是否为 NULL
return st->top;
/*
* 因为是数组,从0 下标开始,但是计数是要 +1 的
* 而top 刚好就是下一个元素的位置与 计数值相同
*/
}
/*
* 判断该栈是否为空栈
*/
bool StackEmpty(ST* st)
{
return 0 == st->top;
/*
* 判断栈是否为空
* 当栈顶 top 为 0 是:表示栈没有一个数据,为空栈,返回 false (0)
* 当栈顶 top 不是 0 时,栈不为空,返回 true (1);
*/
}
void printPath(ST* ps)
{
ST tmp; // 定义一个临时拷贝的栈,用于将原栈终点坐标,放入到 tmp 栈的栈底位置,
StackInit(&tmp); // 初始化 临时栈
// 将原来栈的坐标内容 拷贝到 临时栈中(终点作为栈底)
while (!StackEmpty(ps))
{
StackPush(&tmp, StackTop(ps)); // 将原栈的栈顶坐标 ,入栈到tmp临时栈中
StackPop(ps); // 出栈,取栈顶元素后,需要出栈,不然无法取到栈后面的数据
}
//
while (!StackEmpty(&tmp))
{
PT top = StackTop(&tmp); // 取出临时 tmp 栈中的栈顶坐标内容
printf("(%d,%d)\n", top.x, top.y); // 打印坐标
StackPop(&tmp); // 出栈,取栈顶要与出栈一起,不然无法取到栈后面的数据
}
StackDestory(&tmp); // 销毁临时栈
}
// 判断当前迷宫中的坐标是否存在越界/障碍物,
bool lsPass(int** mace, int N, int M, PT pos)
{
if ((pos.x >= 0 && pos.x < N)
&& (pos.y >= 0 && pos.y < M) // 没有越界
&& (mace[pos.x][pos.y] == 0)) // 不是障碍物
{
return true;
}
else
{
return false;
}
}
首先我们需要创建一个 二维数组 用于存放输入的迷宫结构。
关于 C语言 二维数组 的实现,因为我们是需要,通过输入数值来确定迷宫的大小的。
但是在 C语言中,数组的大小必须使用常量值: 1, 2, 3 才行,变量是不可以的 ,如下图:
所以我们只能通过 动态开辟空间创建二维数组 了,才行
使用 指针数组
,重点是 数组 ,指针数组: 存放指针的地址的数组。因为是存放的是指针的地址所以,该指针数组是一个 双指针数组 ,
通过函数 malloc
内存堆区上开辟空间,malloc
在堆区上开辟空间,返回开辟的空间的 首地址
,
因为题目中可能输入多组数组迷宫,所以这里我们需要使用上循环的输入scanf("") != EOF
,EOF 数值上是 1,可以理解为是 停止输入的一个标记
具体代码如下:
int main()
{
int N = 0;
int M = 0;
while (scanf("%d%d", &N, &M) != EOF) // 输入二维数组的长宽, EOF 不等于 -1 无限输入
{
// int arr[N][M] 这样是不行的 在数组里面中的常量必须是,常量值 1,2,3
// 所以我们需要使用动态开辟二维数组:
// 使用指针数组,重点是指针, 指针数组用于存放指针的地址,
int** maze = (int**)malloc(sizeof(int*) * N);
// int** 双指针类型, 指针数组,数组元素是 指针的地址
// malloc 返回堆区上开辟的空间的首地址, (int**) 强制转换为双指针,因为我们
// malloc 开辟类型是 (int*) 指针的地址,自然要使用 双指针才能保存到 指针的地址
// sizeof(int*) 该指针类型的大小, *
// 判断堆区上空间开辟是否成功
if (NULL == maze)
{
perror("maze malloc error"); // 错误提醒
exit(-1); // 程序非正常结束
}
for (int i = 0; i < N; i++)
{
maze[i] = (int*)malloc(sizeof(int) * M); // 堆区上开辟空间
//判断堆区上空间开辟是否成功
if (NULL == maze[i])
{
perror("maze[i] malloc error");
exit(-1);
}
}
// 迷宫构造的输入:
for (int i = 0; i < N; i++)
{
for (int j = 0; j < M; j++)
{
scanf("%d", &maze[i][j]);
}
}
StackInit(&path); // 初始化 path全局栈
PT entry = { 0,0 }; // 定义初始坐标;为 0,0 结构体可以使用花括号全部初始赋值
if (getMazePath(maze, N, M, entry)) // 递归坐标位置上下左右
{
printPath(&path); // 打印路线坐标
}
else
{
printf("没有找到通路\n");
}
StackDestory(&path); // 销毁全局栈;
/*
* 注意我们这里是通过动态开辟的二维数组(指针数组),
* 要先将存放到指针数组中的指针的地址的空间先释放掉,再释放指针数组本身的地址空间
* 如果先释放了指针数组本身的地址空间的话,存放在指针数组中的指针的地址就无法再找到了,成了野指针了
* 更是无从释放了.
*/
for (int i = 0; i < N; i++) // 释放指针数组中存放的指针的地址
{
free(maze[i]);
}
free(maze);
maze = NULL;
}
return 0;
}
上述代码解析:
while (scanf("%d%d", &N, &M) != EOF)
表示:可以输入多组数据,当不输入数值时(= EOF), 结束循环int** maze = (int**)malloc(sizeof(int*) * N);
创建双指针数组,malloc
堆区上开辟空间返回,开辟空间的首地址,这里开辟的是 指针的地址空间 ,所以我们需要 双指针 来保存指针的地址,强制转换为
(int**)
PT entry = { 0,0 };
从入口开始;坐标 (0,0)- 最后释放堆区上开辟的空间,我们需要先将保存在二维双指针数组中元素的指针的地址的空间,再释放
二维双指针数组本身的地址空间,因为如果你释放二维双指针数组本身的地址空间的话,保存在二维数组中
的指针的地址就再也无法找到了,成了野指针了。
是返回 false
具体代码实现:
// 判断当前迷宫中的坐标是否存在越界/障碍物,
bool lsPass(int** mace, int N, int M, PT pos)
{
if ((pos.x >= 0 && pos.x < N)
&& (pos.y >= 0 && pos.y < M) // 没有越界
&& (mace[pos.x][pos.y] == 0)) // 不是障碍物
{
return true;
}
else
{
return false;
}
}
入口——> 出口
的中的坐标存入栈中的,栈的特性是先进后出 的特点。而我们最后入栈的是出口的坐标 ,导致先从栈取到的是出口的坐标位置,不合题意:入口——> 出口 的坐标,
所以我们需要通过一个创建一个临时栈,将存放栈顶为出口 的栈的坐标内容,导入到临时栈中,这样临时栈中的
栈顶坐标是入口 ,栈底是出口 ,再将临时栈中的拷贝到的坐标数据,循环取出就是,入口——> 出口
的坐标了。
具体代码如下:
void printPath(ST* ps)
{
ST tmp; // 定义一个临时拷贝的栈,用于将原栈终点坐标,放入到 tmp 栈的栈底位置,
StackInit(&tmp); // 初始化 临时栈
// 将原来栈的坐标内容 拷贝到 临时栈中(终点作为栈底)
while (!StackEmpty(ps))
{
StackPush(&tmp, StackTop(ps)); // 将原栈的栈顶坐标 ,入栈到tmp临时栈中
StackPop(ps); // 出栈,取栈顶元素后,需要出栈,不然无法取到栈后面的数据
}
//
while (!StackEmpty(&tmp))
{
PT top = StackTop(&tmp); // 取出临时 tmp 栈中的栈顶坐标内容
printf("(%d,%d)\n", top.x, top.y); // 打印坐标
StackPop(&tmp); // 出栈,取栈顶要与出栈一起,不然无法取到栈后面的数据
}
StackDestory(&tmp); // 销毁临时栈
}
上述代码的注意事项
注意取了栈顶
StackTop(ps)
元素后,紧跟着需要StackPop(&tmp)
出栈,不然无法取到栈后面的数据内容。
上下左右
,每次都要讲移动的坐标保存到栈中,同时判断该坐标是否,到达了,终点右下角
,到达终点,返回 true ,每移动一个坐标都需要判断该坐标是否越界,是否是障碍物,最后需要将不是走向终点的坐标,,出栈,防止后面,取栈的数据,不是从入口——> 出口
的坐标了
具体代码如下:
// 坐标的上下左右的移动
bool getMazePath(int** maze, int N, int M, PT cur)
{
StackPush(&path, cur); // path 全局栈,cur 表示的是当前位置的坐标位置, 存入栈中
// 判断该坐标位置是否达到终点位置
if (cur.x == N - 1 && cur.y == M - 1)
{
return true;
}
PT next; // 下一个临时坐标,用于保存当前(cur) 的坐标用于后面的移动
maze[cur.x][cur.y] = 2; // 标记走过的坐标,防止往回走,形成死循环
// 上走
next = cur; //next = cur;
next.x = next.x - 1; // next.x = next.x - 1;
if (lsPass(maze, N, M, next)) // 判断当前上走后的坐标位置(越界/障碍物),可否继续
{
if (getMazePath(maze, N, M, next)) // 可以继续,递归当前上走后的坐标位置,后的上下左右
{
return true;
}
}
// 下走
next = cur;
next.x = next.x + 1;
if (lsPass(maze, N, M, next)) // 判断当前下走后的坐标位置(越界/障碍物),可否继续
{
if (getMazePath(maze, N, M, next)) // 可以继续,递归当前下后的坐标位置,后的上下左右
{
return true; // 到达终点返回
}
}
// 向左走
next = cur;
next.y = next.y - 1;
if (lsPass(maze, N, M, next)) // 判断当前左走后的坐标位置(越界/障碍物),可否继续
{
if (getMazePath(maze, N, M, next)) // 可以继续,递归当前左走后的坐标位置上下左右
{
return true;
}
}
// 向右走
next = cur;
next.y = next.y + 1;
if (lsPass(maze, N, M, next)) // 判断当前右走后的坐标位置(越界/障碍物),可否继续
{
if (getMazePath(maze, N, M, next)) // 可以继续,递归当前右走后的坐标位置上下左右
{
return true;
}
}
StackPop(&path); // 将保存在栈中不可以走的坐标位置,出栈掉
return false;
}
上述代码解析:
StackPush(&path, cur);
表示:path 全局栈,cur 表示的是当前位置的坐标位置, 存入栈中if (cur.x == N - 1 && cur.y == M - 1)
表示:到达终点,右下角if (lsPass(maze, N, M, next))
表示: 判断当前右走后的坐标位置(越界/障碍物),可否继续if (getMazePath(maze, N, M, next))
表示:可以继续,递归当前的坐标位置,后的上下左右StackPop(&path);
: 将保存在栈中不可以走的坐标位置,出栈掉
#define _CRT_SECURE_NO_WARNINGS 1
#include
#include // 断言
#include
#include // bool 布尔值
// 定义坐标结构体
typedef struct Postion
{
int x; // 数组坐标行
int y; // 数组坐标列
}PT;
#define OCE 2 // 扩容的倍数
typedef PT STDatatype; // 定义该栈中的数据类型,定义为 PT坐标结构类型
// 输出栈里面的保存的路径
/*
* 栈是: 后进的先出去,而我们要的是从起点 ———> 终点的一路的坐标
* 因为我们最后入栈的是终点,栈的原理:后进先出,先从栈取出来的是终点坐标,不合题意
* 所以我们: 将原来栈的保存的坐标,导入到另外一个栈中,从而将终点坐标放入到栈底了。
* 这样我们将 拷贝的临时栈中的坐标内容打印出来,就是从起点 ——> 终点的一路的坐标路线。
*/
/*
* 创建栈的结构体
*/
typedef struct Stack
{
STDatatype* data; //动态顺序表开辟空间
int top; // 栈顶的标识
int copacity; // 栈容量
}ST;
ST path; // 全局栈
/* 函数,变量的声明 extern
* 声明是不会开辟空间的,所以声明可以多次
* 而定义是会开辟空间的,所以定义只能有一次
* 开辟空间不是目的,存放数据才是目的
* 因为只有开辟了空间,才可以存放数据
*
*/
extern void StackInit(ST* st); // 初始化栈
extern void StackDestory(ST* st); // 销毁栈
extern void StackPush(ST* st, STDatatype x); // 栈顶入栈操作
extern void StackPop(ST* st); // 栈顶出栈操作
extern STDatatype StackTop(ST* st); // 取栈顶元素
extern int StackSize(ST* st); // 求栈中存在的数据个数
extern bool StackEmpty(ST* st); // 判断栈是否为空
//extern void TestStack(ST* st); // 对于栈的测试
/*
* 初始化栈
*/
void StackInit(ST* st)
{
assert(st); // 断言,判断st 不为 NULL;
st->data = (STDatatype*)malloc(sizeof(STDatatype) * OCE);
// 判断动态内存(堆上)开辟的空间是否成功
if (NULL == st->data)
{
perror("st->data 动态开辟空间");
exit(-1); // 非正常中止程序
}
st->copacity = 2; // 当前栈的容量
st->top = 0; // 栈顶位置
/*
* top = 0 ,表示的是栈顶的位置是该最后元素的下一个元素的位置
* top = 1; 表示的是栈顶的位置就是该最后元素的位置
*/
}
/*
* 销毁栈
*/
void StackDestory(ST* st)
{
assert(st); // 断言,st是否为空
free(st->data); // 释放该内存空间
st->data = NULL; // 手动置为NULL,防止非法访问
st->copacity = 0;
st->top = 0;
}
// 栈顶入栈操作
void StackPush(ST* st, STDatatype x)
{
assert(st); // 断言判断,st是否为NULL
// 判断栈是否满了,满了扩容
if (st->copacity == st->top)
{
// 动态(堆区)扩容开辟空间
STDatatype* tmp = (STDatatype*)realloc(st->data, st->copacity * OCE * sizeof(STDatatype));
// 判断动态内存开辟空间是否成功
if (NULL == tmp)
{
perror("tmp 扩容"); // 打印错误报告
exit(-1); // 非正常退出
}
else
{
// 动态开辟空间成功,转移主权
st->data = tmp;
st->copacity *= OCE; // 栈容量扩张
}
}
st->data[st->top] = x; // 栈顶入栈数值
st->top++; // 栈顶加加
}
/*
* 栈顶出栈操作
*/
void StackPop(ST* st)
{
assert(st); // 断言判断,st 是否为 NULL
assert(st->top > 0); // 出栈的位置必须大于 0 ,不能一直删除没了吧
st->top--; // 栈顶位置减减一下,就好了
/*
* 注意不可以把 st->data[st->top] = 0; 置为 0 的方式
* 因为可能你存放的数值就是 0 呢,不就有误导了吗
*/
}
/*
* 取出栈顶元素
*/
STDatatype StackTop(ST* st)
{
assert(st); // 断言判断,st 是否为NULL
assert(st->top > 0); // 栈不为空
return st->data[st->top - 1];
/*
* -1 ,是因为这里的top 初始化是 0
* 其top所指向的位置是 该最后一个元素的下一个位置
*/
}
/*
* 求出栈顶元素的个数
*/
int StackSize(ST* st)
{
assert(st); // 断言,判断st 是否为 NULL
return st->top;
/*
* 因为是数组,从0 下标开始,但是计数是要 +1 的
* 而top 刚好就是下一个元素的位置与 计数值相同
*/
}
/*
* 判断该栈是否为空栈
*/
bool StackEmpty(ST* st)
{
return 0 == st->top;
/*
* 判断栈是否为空
* 当栈顶 top 为 0 是:表示栈没有一个数据,为空栈,返回 false (0)
* 当栈顶 top 不是 0 时,栈不为空,返回 true (1);
*/
}
void printPath(ST* ps)
{
ST tmp; // 定义一个临时拷贝的栈,用于将原栈终点坐标,放入到 tmp 栈的栈底位置,
StackInit(&tmp); // 初始化 临时栈
// 将原来栈的坐标内容 拷贝到 临时栈中(终点作为栈底)
while (!StackEmpty(ps))
{
StackPush(&tmp, StackTop(ps)); // 将原栈的栈顶坐标 ,入栈到tmp临时栈中
StackPop(ps); // 出栈,取栈顶元素后,需要出栈,不然无法取到栈后面的数据
}
//
while (!StackEmpty(&tmp))
{
PT top = StackTop(&tmp); // 取出临时 tmp 栈中的栈顶坐标内容
printf("(%d,%d)\n", top.x, top.y); // 打印坐标
StackPop(&tmp); // 出栈,取栈顶要与出栈一起,不然无法取到栈后面的数据
}
StackDestory(&tmp); // 销毁临时栈
}
// 判断当前迷宫中的坐标是否存在越界/障碍物,
bool lsPass(int** mace, int N, int M, PT pos)
{
if ((pos.x >= 0 && pos.x < N)
&& (pos.y >= 0 && pos.y < M) // 没有越界
&& (mace[pos.x][pos.y] == 0)) // 不是障碍物
{
return true;
}
else
{
return false;
}
}
// 坐标的上下左右的移动
bool getMazePath(int** maze, int N, int M, PT cur)
{
StackPush(&path, cur); // path 全局栈,cur 表示的是当前位置的坐标位置, 存入栈中
// 判断该坐标位置是否达到终点位置
if (cur.x == N - 1 && cur.y == M - 1)
{
return true;
}
PT next; // 下一个临时坐标,用于保存当前(cur) 的坐标用于后面的移动
maze[cur.x][cur.y] = 2; // 标记走过的坐标,防止往回走,形成死循环
// 上走
next = cur; //next = cur;
next.x = next.x - 1; // next.x = next.x - 1;
if (lsPass(maze, N, M, next)) // 判断当前上走后的坐标位置(越界/障碍物),可否继续
{
if (getMazePath(maze, N, M, next)) // 可以继续,递归当前上走后的坐标位置,后的上下左右
{
return true;
}
}
// 下走
next = cur;
next.x = next.x + 1;
if (lsPass(maze, N, M, next)) // 判断当前下走后的坐标位置(越界/障碍物),可否继续
{
if (getMazePath(maze, N, M, next)) // 可以继续,递归当前下后的坐标位置,后的上下左右
{
return true; // 到达终点返回
}
}
// 向左走
next = cur;
next.y = next.y - 1;
if (lsPass(maze, N, M, next)) // 判断当前左走后的坐标位置(越界/障碍物),可否继续
{
if (getMazePath(maze, N, M, next)) // 可以继续,递归当前左走后的坐标位置上下左右
{
return true;
}
}
// 向右走
next = cur;
next.y = next.y + 1;
if (lsPass(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) // 输入二维数组的长宽, EOF 不等于 -1 无限输入
{
// int arr[N][M] 这样是不行的 在数组里面中的常量必须是,常量值 1,2,3
// 所以我们需要使用动态开辟二维数组:
// 使用指针数组,重点是指针, 指针数组用于存放指针的地址,
int** maze = (int**)malloc(sizeof(int*) * N);
// int** 双指针类型, 指针数组,数组元素是 指针的地址
// malloc 返回堆区上开辟的空间的首地址, (int**) 强制转换为双指针,因为我们
// malloc 开辟类型是 (int*) 指针的地址,自然要使用 双指针才能保存到 指针的地址
// sizeof(int*) 该指针类型的大小, *
// 判断堆区上空间开辟是否成功
if (NULL == maze)
{
perror("maze malloc error"); // 错误提醒
exit(-1); // 程序非正常结束
}
for (int i = 0; i < N; i++)
{
maze[i] = (int*)malloc(sizeof(int) * M); // 堆区上开辟空间
//判断堆区上空间开辟是否成功
if (NULL == maze[i])
{
perror("maze[i] malloc error");
exit(-1);
}
}
// 迷宫构造的输入:
for (int i = 0; i < N; i++)
{
for (int j = 0; j < M; j++)
{
scanf("%d", &maze[i][j]);
}
}
StackInit(&path); // 初始化 path全局栈
PT entry = { 0,0 }; // 定义初始坐标;为 0,0 结构体可以使用花括号全部初始赋值
if (getMazePath(maze, N, M, entry))
{
printPath(&path); // 打印路线坐标
}
else
{
printf("没有找到通路\n");
}
StackDestory(&path); // 销毁全局栈;
/*
* 注意我们这里是通过动态开辟的二维数组(指针数组),
* 要先将存放到指针数组中的指针的地址的空间先释放掉,再释放指针数组本身的地址空间
* 如果先释放了指针数组本身的地址空间的话,存放在指针数组中的指针的地址就无法再找到了,成了野指针了
* 更是无从释放了.
*/
for (int i = 0; i < N; i++) // 释放指针数组中存放的指针的地址
{
free(maze[i]);
}
free(maze);
maze = NULL;
}
return 0;
}
下面是难度上升的,迷宫问题:
原题目链接: 🔜🔜🔜 地下迷宫_滴滴笔试题_牛客网 (nowcoder.com)
#include
#include
#include
#include // bool 布尔值
typedef struct Postion
{
int row;
int col;
}PT;
/
typedef PT STDataType;
typedef struct Stack
{
STDataType* a;
int top;
int capacity;
}ST, Stack;
void StackInit(ST* ps);
void StackDestory(ST* ps);
// 入栈
void StackPush(ST* ps, STDataType x);
// 出栈
void StackPop(ST* ps);
STDataType StackTop(ST* ps);
int StackSize(ST* ps);
bool StackEmpty(ST* ps);
void StackInit(ST* ps)
{
assert(ps);
ps->a = (STDataType*)malloc(sizeof(STDataType) * 4);
if (ps->a == NULL)
{
printf("malloc fail\n");
exit(-1);
}
ps->capacity = 4;
ps->top = 0;
}
void StackDestory(ST* ps)
{
assert(ps);
free(ps->a);
ps->a = NULL;
ps->top = ps->capacity = 0;
}
// 入栈
void StackPush(ST* ps, STDataType x)
{
assert(ps);
// 满了-》增容
if (ps->top == ps->capacity)
{
STDataType* tmp = (STDataType*)realloc(ps->a, ps->capacity * 2 * sizeof(STDataType));
if (tmp == NULL)
{
printf("realloc fail\n");
exit(-1);
}
else
{
ps->a = tmp;
ps->capacity *= 2;
}
}
ps->a[ps->top] = x;
ps->top++;
}
// 出栈
void StackPop(ST* ps)
{
assert(ps);
// 栈空了,调用Pop,直接中止程序报错
assert(ps->top > 0);
//ps->a[ps->top - 1] = 0;
ps->top--;
}
STDataType StackTop(ST* ps)
{
assert(ps);
// 栈空了,调用Top,直接中止程序报错
assert(ps->top > 0);
return ps->a[ps->top - 1];
}
int StackSize(ST* ps)
{
assert(ps);
return ps->top;
}
bool StackEmpty(ST* ps)
{
assert(ps);
return ps->top == 0;
}
Stack path;
Stack minpath;
void PrintMaze(int** maze, int N, int M)
{
for (int i = 0; i < N; ++i)
{
for (int j = 0; j < M; ++j)
{
printf("%d ", maze[i][j]);
}
printf("\n");
}
printf("\n");
}
// 输出栈里面的坐标路径
void PirntPath(Stack* ps)
{
// path数据倒到rPath
Stack rPath;
StackInit(&rPath);
while (!StackEmpty(ps))
{
StackPush(&rPath, StackTop(ps));
StackPop(ps);
}
while (StackSize(&rPath) > 1)
{
PT top = StackTop(&rPath);
printf("[%d,%d],", top.row, top.col);
StackPop(&rPath);
}
PT top = StackTop(&rPath);
printf("[%d,%d]", top.row, top.col);
StackPop(&rPath);
StackDestory(&rPath);
}
bool IsPass(int** maze, int N, int M, PT pos)
{
if (pos.row >= 0 && pos.row < N
&& pos.col >= 0 && pos.col < M
&& maze[pos.row][pos.col] == 1)
{
return true;
}
else
{
return false;
}
}
void StackCopy(Stack* ppath, Stack* pcopy)
{
pcopy->a = (STDataType*)malloc(sizeof(STDataType*)*ppath->capacity);
memcpy(pcopy->a, ppath->a, sizeof(STDataType)*ppath->top);
pcopy->top = ppath->top;
pcopy->capacity = ppath->capacity;
}
void GetMazePath(int** maze, int N, int M, PT 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位置得上下左右四个方向
PT next;
maze[cur.row][cur.col] = 2;
// 上
next = cur;
next.row -= 1;
if (IsPass(maze, N, M, next))
{
GetMazePath(maze, N, M, next, P - 3);
}
// 下
next = cur;
next.row += 1;
if (IsPass(maze, N, M, next))
{
GetMazePath(maze, N, M, next, P);
}
// 左
next = cur;
next.col -= 1;
if (IsPass(maze, N, M, next))
{
GetMazePath(maze, N, M, next, P - 1);
}
// 右
next = cur;
next.col += 1;
if (IsPass(maze, N, M, next))
{
GetMazePath(maze, N, M, next, P - 1);
}
// 恢复一下
maze[cur.row][cur.col] = 1;
StackPop(&path);
}
int main()
{
int N = 0, M = 0, P = 0;
while (scanf("%d%d%d", &N, &M, &P) != EOF)
{
// int a[n][m]; // vs2013 不支持
// 动态开辟二维数组
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);
// PrintMaze(maze, N, M);
PT entry = { 0, 0 };
GetMazePath(maze, N, M, entry, P);
if(!StackEmpty(&minpath))
{
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;
}
限于自身水平,其中存在的错误,希望大家给予指教,韩信点兵——多多益善,谢谢大家,后会有期,江湖再见 !!!