【C语言】迷宫问题

news2024/11/16 21:23:46

【C语言】迷宫问题

  • 一. 题目描述
  • 二. 思想
    • 2.1 算法---回溯算法
    • 2.2 思路分析+图解
  • 三. 代码实现
    • 3.1 二维数组的实现
    • 3.2 上下左右四个方向的判断
    • 3.4 用栈记录坐标的实现
    • 3.5 完整代码
  • 四. 总结

一. 题目描述

在这里插入图片描述

牛客网链接:https://www.nowcoder.com/questionTerminal/cf24906056f4488c9ddb132f317e03bc

在这里插入图片描述

二. 思想

2.1 算法—回溯算法

  • 回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。

  • 回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。

  • 许多复杂的,规模较大的问题都可以使用回溯法,有“通用解题方法”的美称。(深度优先遍历,能走则走,不能走则退)
    在这里插入图片描述

2.2 思路分析+图解

  • 对于不支持变长数组的C版本来说,如果需要实现二维变长数组的话,就需要动态开辟二维数组。

  • 每次需要对上下左右四个方向进行判断(但是四个方向选择的先后顺序不是很重要,每个迷宫不一样),才能到达下一个坐标,并且如果四个方向都不能走而且还没到达终点,那么就要往回走。

  • 为了判断往回是否还有其它能走的方向,我们需要对走过的坐标进行标记(已走过的坐标,用数字 2 标记),这样就不会出现走重复的路线。

  • 在结束的时候,我们需要打印这条路径的每个坐标,那么我们就需要对我们走过的坐标进行储存,对到达不了终点的路上的坐标消除,所以似乎需要栈来帮我们储存这个坐标。

在这里插入图片描述

三. 代码实现

3.1 二维数组的实现

在这里插入图片描述

void PrintMase(int** mase, int N, int M)
{
	for (int i = 0;i < N;i++)//先确定行---一维数组
	{
		for (int j = 0;j < M;j++)
		{
			printf("%d ", mase[i][j]);
		}
		printf("\n");
	}
}

3.2 上下左右四个方向的判断

  • 从(0,0)坐标开始,假设先判断上下,再判断左右,如果能通过就移动坐标,并且进入下次判断,直到到达终点,或者不能移动,并且需要对每次到达的坐标进行标记,假设标记为2。

  • 如果四个方向都不能走,并且没到达终点,那么将一直返回上一个位置,直到有其它方向能走。

  • 如果到达终点,返回true,直到跳出所有递归。

bool GetMasePath(int** maze, int N, int M)
{
	StackPush(&path, cur);//cur表示当前坐标
	if (cur.row == N - 1 && cur.col == M - 1)//当前坐标为右下时,走出迷宫
	{
		return true;
	}

	//走过的标志为2
	PT next;
	maze[cur, row][cur.col] == 2;

	//上
	next = cur;
	next.row -= 1;//行减1
	if (IsPass(mzse, N, M, next))
	{
		if (GetMasePath(maze, N, M, next))
		{
			return true;
		}
	}

	//下
	next = cur;
	next.row += 1;//行加1
	if (IsPass(mzse, N, M, next))
	{
		if (GetMasePath(maze, N, M, next))
		{
			return true;
		}
	}

	//左
	next = cur;
	next.col -= 1;//列减1
	if (IsPass(mzse, N, M, next))
	{
		if (GetMasePath(maze, N, M, next))
		{
			return true;
		}
	}

	//右
	next = cur;
	next.col += 1;//列加1
	if (IsPass(mzse, N, M, next))
	{
		if (GetMasePath(maze, N, M, next))
		{
			return true;
		}
	}
}

3.4 用栈记录坐标的实现

由于C语言没有自己的栈,所以要自己搭建一个栈。

  • 每次判断坐标前,先将当前坐标存入栈中,如果四个方向都不能走的时候,再出栈。

  • 当到达终点,返回完后,对栈中数据进行处理。

  • 因为栈中数据打印后与题目要求的打印相反,所有需要再创建一个栈,将当前栈中的数据导入另一个栈中,从而实现相反的打印。

为什么要建立两个栈?

因为题目要求的打印路径顺序是有先后的,只有一个栈是,路径坐标是倒的,与题目要求相反;所以建立两个栈,使得坐标顺序不变

typedef PT STDataType;

typedef struct Stack
{
	STDataType* a;
	int top;
	int capacity;//空间
}ST;

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);//bool 判断是否栈空,栈空为1


//初始化
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);//再分配
		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);
	assert(ps->top > 0);

	ps->top--;
}



//返回栈顶
STDataType StackTop(ST* ps)
{
	assert(ps);
	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;
/

//打印迷宫
void PrintMase(int** mase, int N, int M)
{
	for (int i = 0;i < N;i++)//先确定行---一维数组
	{
		for (int j = 0;j < M;j++)
		{
			printf("%d ", mase[i][j]);
		}
		printf("\n");
	}
}


//输出栈中坐标路径
void PrintPath(Stack* ps)
{
	//把path数据倒置入rPath
	Stack rPath;
	StackInit(&rPath);
	while (!StackEmpty(&Path)
	{
		StackPush(&rPath, StackTop(&Path));
		StackPop(&path)
	}
	while (!StackEmpty(&rPath)
	{
		PT top = StackTop(&rPath);
		printf("(%d , %d)\n", top.row, top.col);
		StackPop(&rpath)
	}
}

//判读路径是否正确
bool IsPass(int** maze, int N, int M)
{
	if (pos.row >= 0 && pos.row < N && pos.col >= 0 
		&& pos.col < M 
		&& masz[pos.row][pos.col] == 0)
	{
		return true;
	}
	else
	{
		return false;
	}
}

3.5 完整代码

#include<stdio.h>
#include<assert.h>
#include<stdlib.h>

typedef struct Postion
{
	int row;
	int col;
}PT;
//

typedef PT STDataType;

typedef struct Stack
{
	STDataType* a;
	int top;
	int capacity;//空间
}ST;

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);//bool 判断是否栈空,栈空为1

//初始化
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);//再分配
		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);
	assert(ps->top > 0);

	ps->top--;
}

//返回栈顶
STDataType StackTop(ST* ps)
{
	assert(ps);
	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;
/

//打印迷宫
void PrintMase(int** mase, int N, int M)
{
	for (int i = 0;i < N;i++)//先确定行---一维数组
	{
		for (int j = 0;j < M;j++)
		{
			printf("%d ", mase[i][j]);
		}
		printf("\n");
	}
}

//输出栈中坐标路径
void PrintPath(Stack* ps)
{
	//把path数据倒置入rPath
	Stack rPath;
	StackInit(&rPath);
	while (!StackEmpty(&Path)
	{
		StackPush(&rPath, StackTop(&Path));
		StackPop(&path)
	}
	while (!StackEmpty(&rPath)
	{
		PT top = StackTop(&rPath);
		printf("(%d , %d)\n", top.row, top.col);
		StackPop(&rpath)
	}
}

//判读路径是否正确
bool IsPass(int** maze, int N, int M)
{
	if (pos.row >= 0 && pos.row < N && pos.col >= 0 
		&& pos.col < M 
		&& masz[pos.row][pos.col] == 0)
	{
		return true;
	}
	else
	{
		return false;
	}
}

//上下左右遍历
bool GetMasePath(int** maze, int N, int M)
{
	StackPush(&path, cur);//cur表示当前坐标
	if (cur.row == N - 1 && cur.col == M - 1)//当前坐标为右下时,走出迷宫
	{
		return true;
	}

	//走过的标志为2
	PT next;
	maze[cur, row][cur.col] == 2;

	//上
	next = cur;
	next.row -= 1;//行减1
	if (IsPass(mzse, N, M, next))
	{
		if (GetMasePath(maze, N, M, next))
		{
			return true;
		}
	}

	//下
	next = cur;
	next.row += 1;//行加1
	if (IsPass(mzse, N, M, next))
	{
		if (GetMasePath(maze, N, M, next))
		{
			return true;
		}
	}

	//左
	next = cur;
	next.col -= 1;//列减1
	if (IsPass(mzse, N, M, next))
	{
		if (GetMasePath(maze, N, M, next))
		{
			return true;
		}
	}

	//右
	next = cur;
	next.col += 1;//列加1
	if (IsPass(mzse, N, M, next))
	{
		if (GetMasePath(maze, N, M, next))
		{
			return true;
		}
	}
}

int main()
{
	int N = 0, M = 0;
	while (scanf("%d%d", &N, &M) != 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);
		// PrintMaze(maze, N, M);
		PT entry = { 0, 0 };
		if (GetMazePath(maze, N, M, entry))
		{
			//printf("找到通路\n");
			PirntPath(&path);
		}
		else
		{
			printf("没有找到通路\n");
		}

		StackDestory(&path);

		for (int i = 0; i < N; ++i)
		{
			free(maze[i]);
		}
		free(maze);
		maze = NULL;
	}

	return 0;
}

四. 总结

迷宫问题的难点:

  1. 建立两个栈,使得打印的顺序是正确的
  2. 怎么找路径,标记已经走过的坐标
  3. 栈的应用
  4. 递归
  5. 回溯算法

感谢阅读

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/419922.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

STM32看门狗

目录 独立看门狗 IWDG 什么是看门狗&#xff1f; 独立看门狗本质 独立看门狗框图 独立看门狗时钟 分频系数算法&#xff1a; ​编辑 重装载寄存器 键寄存器 溢出时间计算公式 独立看门狗实验 需求&#xff1a; 硬件接线&#xff1a; 溢出时间计算&#xff1…

macOS设置环境变量和别名

因为我的mac所用shell是bash&#xff0c;所以本文中涉及的环境变量和别名配置均在~/.zshrc文件中,且在每次配置完成后&#xff0c;需要执行source ~/.zshrc命令使配置文件生效 环境变量 通过配置环境变量&#xff0c;我们可以将某个路径暴露到全局&#xff0c;这样可以在全局…

周总结(第一周)

3月份3个星期 *** 三个星代表不会 ** 再做 * 加强 题目1-完全二叉树(记忆) 考察数据结构 完全二叉树的深度deplog2(N1)1 完全二叉树节点的深度depiceil(log2(i1))向上舍入 完全二叉树的层次遍历&#xff0c;遍历每层的二叉树计算基础每层的总和&#xff0c;然后找出最大的和…

Talk预告 | 新加坡国立大学郑奘巍 AAAI‘23 杰出论文:大批量学习算法加速推荐系统训练

本期为TechBeat人工智能社区第486期线上Talk&#xff01; 北京时间3月30日(周四)20:00&#xff0c;新加坡国立大学二年级博士生——郑奘巍的Talk将准时在TechBeat人工智能社区开播&#xff01; 他与大家分享的主题是: “大批量学习算法加速推荐系统训练”&#xff0c;届时将分…

Kubernetes 多集群网络方案系列 2 -- Submariner 监控

Submariner 是一个用于连接 Kubernetes 集群的跨集群网络解决方案&#xff0c;可以实现集群之间的服务发现、网络通信等功能。 Prometheus 是一个开源的监控和告警系统&#xff0c;专门用于收集、存储和查询各种应用、系统和基础设施的实时指标数据。Prometheus 具备多维数据模…

Java开发 - MySQL主从复制初体验

前言 前面已经学到了很多知识&#xff0c;大部分也都是偏向于应用方面&#xff0c;在应用实战这条路上&#xff0c;博主一直觉得只有实战才是学习中最快的方式。今天带来主从复制给大家&#xff0c;在刚刚开始动手写的时候&#xff0c;才想到似乎忽略了一些重要的东西&#xf…

面试篇-揭开Spring Bean加载的神秘面纱

SpringBean加载完整过程 启动spring容器&#xff08;创建beanfactory&#xff09;->加载配置(注解、xml)->实例化bean(执行构造方法)->注入依赖->初始化bean&#xff08;设置属性值&#xff09;->使用->销毁 解析和读取 XML 配置文件或注解配置类&#xff0…

Linux嵌入式学习之Ubuntu入门(五)汇编语法学习

系列文章目录 一、Linux嵌入式学习之Ubuntu入门&#xff08;一&#xff09;基本命令、软件安装及文件结构 二、Linux嵌入式学习之Ubuntu入门&#xff08;二&#xff09;磁盘文件介绍及分区、格式化等 三、Linux嵌入式学习之Ubuntu入门&#xff08;三&#xff09;用户、用户组…

synchronized原理、偏向锁、轻量级锁、重量级锁、锁升级

文章目录Synchronized概念自增自减字节码指令临界区竞态条件基本使用原理查看synchronized的字节码指令序列Monitor对象的内存布局Mark Word是如何记录锁状态的偏向锁什么是偏向锁偏向锁延迟偏向偏向锁状态跟踪偏向锁撤销之调用对象HashCode偏向锁撤销之调用wait/notify轻量级锁…

Qt Quick - Drawer

Qt Quick - Drawer使用总结一、概述二、使用1、基础使用2、特点空间运行3、与内容转换相互挤占一、概述 Drawer提供了一个基于滑动的侧边面板&#xff0c;类似于经常在触控界面中使用的侧边面板&#xff0c;为导航提供了一个位置。 二、使用 1、基础使用 抽屉可以放置在内…

springcloud深度探索

中文官方文档&#xff1a;project - Spring Cloud Config - 《Spring Cloud中文文档》 - 书栈网 BookStackSpring Cloud ConfigFeaturesQuick StartSample Projects Spring Cloud为开发人员提供了工具&#xff0c;用以快速的在分布式系统中建立一些通用方案&#xff08;例如配…

CDP思科发现协议解析及C/C++代码实现

通常&#xff0c;大多数网络都有几个路由器或交换机&#xff0c;为了便于网络管理&#xff0c;使用网络图或网络图来告诉网络中存在什么类型的设备&#xff0c;以及所有设备如何相互连接&#xff0c;使用的IP地址以及它们属于哪个VLAN的信息。 CDP是一种专有的第二层思科网络协…

Table Transformer做表格检测和识别实践

计算机视觉方面的三大顶级会议&#xff1a;ICCV,CVPR,ECCV.统称ICE CVPR 2022文档图像分析与识别相关论文26篇汇集简介 论文&#xff1a; PubTables-1M: Towards comprehensive table extraction from unstructured documents是发表于CVPR上的一篇论文 作者发布了两个模型&…

22级ACM 4.16 周赛 题解

这场能题解写的感觉没多少其实&#xff08;真的不是因为懒&#xff09;&#xff0c;既然有人想要题解&#xff0c;那么就随便写一下吧&#xff0c;其实大部分的题都有人写出来&#xff0c;感觉这场真的不需要。 A 题 题解 Count Interval AtCoder - abc233_d_霾まる的博客-CS…

AI绘画王炸功能Control Net安装教程

原文&#xff1a;AI绘画王炸功能Control Net安装教程 - 知乎 AI绘画&#xff0c;最近两大王炸功能出圈了。 一个就是超真实超细节的美女图片&#xff0c;已经快和照片无异了&#xff0c;甚至有人用AI绘画的“女仆照片”开始招募游艇会了&#xff0c;具体教程可以查看Lora这篇…

一键生成元宇宙 AI又杀疯了

人类十几年的进步水平&#xff0c;AI用几个月就能轻易实现。在展示了超强的文本对话能力和一键生图功能后&#xff0c;AI大模型不打算停下&#xff0c;开始挑战搭建3D空间这一更高难度的动作。 这次&#xff0c;Facebook母公司Meta想当一把主导者。几天前&#xff0c;它的首席…

185-二35

Java185-二35单列集合顶层接口collection迭代器增强forlambda表达式list特有方法遍历数据结构数组Linkedlist集合泛型类&#xff0c;泛型方法&#xff0c;泛型结构泛型方法泛型的通配符泛型总结数据结构数据结构&#xff08;二叉树&#xff09;前序遍历数据结构&#xff08;二叉…

crm系统有哪些?具体的功能有哪些?

市面上的CRM系统有很多&#xff0c;例如简道云、销售易、salesforce、纷享销客、SugarCRM等等&#xff0c;这些都是比较知名的&#xff0c;前面也有写过很多关于CRM选型的内容&#xff0c;大家可以点进我的主页翻阅一下。 那么&#xff0c;CRM具体的功能有哪些&#xff1f;下面…

计算机网络 - UDP协议 与 TCP协议可靠性(传输层)

前言 本篇介绍UDP报文格式&#xff0c;认识UDP报文&#xff0c;介绍TCP报文格式&#xff0c;了解TCP可靠性的核心机制&#xff0c;TCP通信中三次握手与四次挥手&#xff1b;如有错误&#xff0c;请在评论区指正&#xff0c;让我们一起交流&#xff0c;共同进步&#xff01; 文…

EDA基础概念

EDA基础概念EDA和CADCAD工具EDA工具EDA技术实现目标可编程逻辑器件简称PLD发展历程FPGA简介CPLD简介FPGA和CPLD区别是否需要同时学习FPGA和CPLDXilinx&#xff08;赛灵思&#xff09;公司介绍&#xff08;AMD收购&#xff09;开发工具Xilinx产品Altera&#xff08;阿尔特拉&…