迷宫问题求解

目录

题目 1:

题目 2:

一、思路

二、代码流程

一、文件准备

二、创建全局变量的栈

三、有效性检验

 四、路径查找

 五、栈的逆序及路径输出

六、题目1 main函数

三、进阶难度

一、全局变量的创建

二、有效性检验

三、栈的深拷贝

四、最优路径的搜索

五、路线输出

六、题目2 main函数

四、总结


链接:迷宫问题__牛客网
来源:牛客网

题目 1:

定义一个二维数组 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

题目 2:

链接:地下迷宫_滴滴笔试题_牛客网
来源:牛客网
 

小青蛙有一天不小心落入了一个地下迷宫,小青蛙希望用自己仅剩的体力值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);
}

六、题目1 main函数

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);
}

六、题目2 main函数

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;
}

四、总结

这便是对数据结构的应用,在遇到不同问题时,选用合适的数据结构来实现目的。

你可能感兴趣的:(数据结构,linq,c#)