牛客网:迷宫问题

%牛客网:迷宫问题

在这里插入图片描述


每博一文案

白居易说,行路难,不在水,不在山,只在人情,反复间,世间
最不可直视的东西,一是太阳,二是人心。人心,大多难猜,真情总是难遇,
很多时候,我们总是自以为自己在对方心目中很重要。可期望越多,失望就越大。
有人说很多人,突然就不再联系了,和重不重要无关,却和需不需要有人。
每个人都有自己的生活,千万别高估自己在别人心目中的地位。
你以为你是别人心中的唯一,说不定,你就是可有可无的人,人心不是石头。
你对别人怎样,别人心里清楚,感情这座天枰,有来有往,才能维持不变,
愿你即像一个勇士,披荆斩棘,又能有人为你遮风挡雨,不需要刻意讨人喜欢
,只要无愧于心,做好自己。余生,别读感情,别猜人心,学会放过,
是善待自己的最好方式。
                                      ——————   一禅心灵庙语


原题:

在这里插入图片描述

有关的题目链接: 🔜🔜🔜 牛客网:迷宫问题


题目分析:

首先我们从题目中知道了,迷宫中只有一个出口,出口的位置是在(右下角) ,开始位置是 (左上角),我们需要通

在这里插入图片描述

算法,找到出口,并把从 入口 ——> 出口 是路线通过坐标打印出来。

这里我们使用的是 C语言 代码解决的。0 为通路,1 为障碍物,不可走。

我们需要遍历整个迷宫,从而找到出口。

在这里插入图片描述

我们需要将,我们已经递归遍历走过的,坐标位置,标记成 2 ,防止,往回走过的路,形成(往前走,再往回走,往前走,再往回走)的死循环。如下图:

在这里插入图片描述


这里我们使用递归的方式解决:而从题目可知,我们需要将从 入口 ——> 出口 是路线通过坐标打印出来。所以我们需要将坐标保存起来,这里我们使用 将,坐标保存起来。因为 C语言 没有,有关栈的库,所以我们只能自己实现一个栈了。大家可以移动到 🔜🔜🔜( 图文并茂 ,栈的实现 —— C语言_ChinaRainbowSea的博客-CSDN博客 可以将栈的实现的代码,复制粘贴过来,是可以直接使用的。


解题的代码实现:

  1. 我们将栈创建出来,并且创建好 坐标的结构体 用于保存对应的坐标位置。

定义一个全局栈: 用于存放我们从 入口 ——> 出口 的路线的坐标。

#include<stdio.h>
#include<assert.h>  // 断言
#include<stdlib.h>  
#include<stdbool.h>   // 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;
	}

}


  1. 首先我们需要创建一个 二维数组 用于存放输入的迷宫结构。

  2. 关于 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)
  • 最后释放堆区上开辟的空间,我们需要先将保存在二维双指针数组中元素的指针的地址的空间,再释放

二维双指针数组本身的地址空间,因为如果你释放二维双指针数组本身的地址空间的话,保存在二维数组中

的指针的地址就再也无法找到了,成了野指针了。


  1. 创建一个函数用于判断当前坐标位置是存在越界,如果越界返回 false ,并且判断该坐标位置是否是障碍物

是返回 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;
	}

}


  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);    // 销毁临时栈
}

上述代码的注意事项

注意取了栈顶StackTop(ps)元素后,紧跟着需要StackPop(&tmp)出栈,不然无法取到栈后面的数据内容。


  1. 最后,递归移动坐标的位置,分别是 上下左右,每次都要讲移动的坐标保存到栈中,同时判断该坐标是否,

到达了,终点右下角,到达终点,返回 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<stdio.h>
#include<assert.h>  // 断言
#include<stdlib.h>  
#include<stdbool.h>   // 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<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>   // 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;
}

在这里插入图片描述


最后:

限于自身水平,其中存在的错误,希望大家给予指教,韩信点兵——多多益善,谢谢大家,后会有期,江湖再见 !!!


在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值