团灭LeetCode跳跃游戏(相关话题:贪心,BFS)

news2024/9/22 21:31:18

目录

LeetCode55跳跃游戏

LeetCode45. 跳跃游戏 II

LeetCode1306. 跳跃游戏 III

LeetCode1345. 跳跃游戏 IV


LeetCode55跳跃游戏

给定一个非负整数数组 nums ,你最初位于数组的 第一个下标 。

数组中的每个元素代表你在该位置可以跳跃的最大长度。

判断你是否能够到达最后一个下标。

示例 1:

输入:nums = [2,3,1,1,4]
输出:true
解释:可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。

示例 2:

输入:nums = [3,2,1,0,4]
输出:false
解释:无论怎样,总会到达下标为 3 的位置。但该下标的最大跳跃长度是 0 , 所以永远不可能到达最后一个下标。
public boolean canJump(int[] nums) {
    int k = 0;
    for (int i = 0; i < nums.length; i++) {
            //k追不上i就认为没法跳到最后一步
            if (i > k) return false;
            k = Math.max(k, i + nums[i]);
     }
     return true;

}

LeetCode45. 跳跃游戏 II

给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]

每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说,如果你在 nums[i] 处,你可以跳转到任意 nums[i + j] 处:

  • 0 <= j <= nums[i] 
  • i + j < n

返回到达 nums[n - 1] 的最小跳跃次数。生成的测试用例可以到达 nums[n - 1]

示例 1:

输入: nums = [2,3,1,1,4]
输出: 2
解释: 跳到最后一个位置的最小跳跃数是2。从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3步到达数组的最后一个位置。

示例 2:

输入: nums = [2,3,0,1,4]
输出: 2
public int jump(int[] nums) {
        int length = nums.length;
        //因为要统计步数所以引入边界值end
        int end = 0;
        int maxPosition = 0; 
        int steps = 0;
        for (int i = 0; i < length - 1; i++) {
            
            maxPosition = Math.max(maxPosition, i + nums[i]); 
            //跳到边界时用maxPosition更新边界end
            //步数steps加1
            if (i == end) {
                end = maxPosition;
                steps++;
            }
        }
        return steps;
}

LeetCode1306. 跳跃游戏 III

这里有一个非负整数数组 arr,你最开始位于该数组的起始下标 start 处。当你位于下标 i 处时,你可以跳到 i + arr[i] 或者 i - arr[i]

请你判断自己是否能够跳到对应元素值为 0 的 任一 下标处。

注意,不管是什么情况下,你都无法跳到数组之外。

示例 1:

输入:arr = [4,2,3,0,3,1,2], start = 5
输出:true
解释:
到达值为 0 的下标 3 有以下可能方案: 
下标 5 -> 下标 4 -> 下标 1 -> 下标 3 
下标 5 -> 下标 6 -> 下标 4 -> 下标 1 -> 下标 3 

示例 2:

输入:arr = [4,2,3,0,3,1,2], start = 0
输出:true 
解释:
到达值为 0 的下标 3 有以下可能方案: 
下标 0 -> 下标 4 -> 下标 1 -> 下标 3

示例 3:

输入:arr = [3,0,2,1,2], start = 2
输出:false
解释:无法到达值为 0 的下标 1 处。 
/**
 * 广度优先,利用used减枝
 **/
public boolean canReach(int[] arr, int start) {

		if (arr[start] == 0) {
			return true;
		}
		int n = arr.length;
        //标记已经访问过的节点
		Set<Integer> used = new HashSet<Integer>();
		used.add(start);
		
		LinkedList<Integer> deque = new LinkedList<>();
		deque.add(start);

		while (deque.size() > 0) {
			Integer u = deque.poll();
			ArrayList<Integer> list = new ArrayList<>();
			list.add(u - arr[u]);
			list.add(u + arr[u]);
			
			for(Integer v: list) {
				if (v >= 0 && v < n && !used.contains(v)) {
					if (arr[v] == 0) {
						return true;
					}
					deque.offer(v);
					used.add(v);
				}
			}
			 
		}
		return false;
}

LeetCode1345. 跳跃游戏 IV

给你一个整数数组 arr ,你一开始在数组的第一个元素处(下标为 0)。

每一步,你可以从下标 i 跳到下标 i + 1 、i - 1 或者 j :

  • i + 1 需满足:i + 1 < arr.length
  • i - 1 需满足:i - 1 >= 0
  • j 需满足:arr[i] == arr[j] 且 i != j

请你返回到达数组最后一个元素的下标处所需的 最少操作次数 。

注意:任何时候你都不能跳到数组外面。

示例 1:

输入:arr = [100,-23,-23,404,100,23,23,23,3,404]
输出:3
解释:那你需要跳跃 3 次,下标依次为 0 --> 4 --> 3 --> 9 。下标 9 为数组的最后一个元素的下标。

示例 2:

输入:arr = [7]
输出:0
解释:一开始就在最后一个元素处,所以你不需要跳跃。

示例 3:

输入:arr = [7,6,9,6,9,6,9,7]
输出:1
解释:你可以直接从下标 0 处跳到下标 7 处,也就是数组的最后一个元素处。

提示:

  • 1 <= arr.length <= 5 * 104
  • -108 <= arr[i] <= 108

直接递归,再通过缓存减枝,结束循环会超时

class Solution {


	//记录从下标0到index处所需的最少步数
    private Map<Integer,Integer> indexCache = new HashMap<Integer,Integer>();

    //key 数组值,value 数组下标
    private Map<Integer,List> numberCache = new HashMap<Integer,List>();

    private Integer minStep=Integer.MAX_VALUE;
 

    public int minJumps(int[] arr) {

          getNumberCache(arr);
          
          minStep(0,0,arr);

          return minStep;
    }

    private void getNumberCache(int[] arr){

       for(int i=0;i<arr.length;i++){
   
            List list = numberCache.get(arr[i]);
            if(list!=null){
                list.add(i);
            }else{
                list = new ArrayList();
                list.add(i);
                numberCache.put(arr[i],list);
            }

       }

    }

    private void minStep(int index,int step,int[] arr){

        if(index==arr.length-1){
            minStep = Math.min(minStep,step);
         }

       
        Integer last = indexCache.get(index);
        if(last!=null){
            //遇到更小的步数更新indexCache的值否则直接结束循环
        	if(step < last) {
        		indexCache.put(index,step);
        	}else {
        		return;
        	}
        }else {
        	indexCache.put(index,step);
        }

        
        List<Integer> params = new ArrayList();

        if(index-1 >= 0){
             params.add(index-1);
        } 
        if(index+1 < arr.length){
             params.add(index+1);
        }
 

        List<Integer> list = numberCache.get(arr[index]);
     
        for(int i=0;i<list.size() && list.size()>1;i++){
            if(index!=list.get(i)){
                  params.add(list.get(i));
            }
        }

        for(int i=0;i< params.size();i++){
            minStep(params.get(i),step+1,arr);
        }
        
    }
}

BFS的思路

根据题目描述,我们可以把给定数组整理成一个无向图,数组中相邻的元素或者元素值相同的元素相连即可。

比如,以示例1,给定的数组 arr = [100,-23,-23,404,100,23,23,23,3,404] 为例,转换成无向图为:

可以看到,我们只需要搜索下标从0到9的最短路径就可以了。

那当然是使用BFS了,图中,我也标记出来了遍历的过程,其实,就是类似于二叉树的层序遍历,BFS本身也是非常擅长最短路的查找的,只需要三层查找就可以找到9这个下标,所以,答案就是3。

理解了这个思路,再看代码就简单多了,我们先整理一下值相同的下标,使用 Map + List 来存储,然后就是BFS遍历,代码的书写方式跟二叉树的层序遍历的模板是一样的,区别是二叉树是有向图,而我们这里是无向图,所以,我们需要一个 visited 记录已经访问过的元素,防止重复访问。

另外,对于值相同的元素访问一次就够了,所以,还加入了一个剪枝的逻辑,只要map中的一个key遍历过,就把它移除防止重复遍历。

public int minJumps2(int[] arr) {
		// 可以看成一张无向图,每个元素与它相邻的元素相连,同时还与它值相同的元素相连
		// 使用BFS从0搜索到n-1即可

		int n = arr.length;
		if (n == 1) {
			return 0;
		}

		// 记录相同值的元素有哪些下标
		Map<Integer, List<Integer>> map = new HashMap<>();
		for (int i = 0; i < n; i++) {
			map.computeIfAbsent(arr[i], v -> new ArrayList<>()).add(i);
		}

		// BFS使用的队列
		Queue<Integer> queue = new LinkedList<>();
		// 记录已经访问过的元素
		// 数组范围:1 <= arr.length <= 5 * 10^4
		// 使用数组比Set效率更优
		// 使用位图更优
		boolean[] visited = new boolean[n];

		queue.offer(0);
		visited[0] = true;

		int ans = 0;
		while (!queue.isEmpty()) {
			// 与二叉树的层序遍历类似
			int size = queue.size();
			while (size-- > 0) {
				int index = queue.poll();

				// 满足条件,直接返回
				if (index == n - 1) {
					return ans;
				}

				// 把它相边的直接入队
				if (map.containsKey(arr[index])) {
					for (int next : map.get(arr[index])) {
						if (next != index && !visited[next]) {
							queue.offer(next);
							visited[next] = true;
						}
						// 剪枝:这个元素相连的都处理过了,后面再遍历到再处理肯定都已经访问过了,不如直接移除,可以减少遍历的次数
						map.remove(arr[index]);
					}
				}

				if (index + 1 <= n - 1 && !visited[index + 1]) {
					queue.offer(index + 1);
					visited[index + 1] = true;
				}

				if (index - 1 >= 0 && !visited[index - 1]) {
					queue.offer(index - 1);
					visited[index - 1] = true;
				}
			}

			ans++;
		}

		// 不会走到这里
		return -1;
}

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

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

相关文章

win32com操作word 第三集:Range精讲(一)

本课程《win32com操作word API精讲&项目实战》&#xff0c;本公众号以文字分享为主&#xff0c;B站与视频号则发布视频分享&#xff0c;ID均为&#xff1a;一灯编程 本集开始&#xff0c;将会深入Document接口。打开或创建一个文档都会产生一个Document对象&#xff0c;它代…

十大排序(Java版本)

排序分为比较排序和非比较排序两种&#xff0c;常见的排序为比较排序&#xff0c;共有七类&#xff1a;直接插入排序、希尔排序、选择排序、冒泡排序、堆排序、快速排序以及归并排序。另有三种非基于比较类的排序&#xff1a;计数排序、基数排序和桶排序。基于比较的排序直接插…

TreeMap和TreeSet的介绍

目录 1、认识 TreeMap 和 TreeSet 2、TreeMap 的主要成员变量 3、TreeMap 的主要构造方法 4、TreeMap 和 TreeSet 的元素必须可比较 5、TreeMap 和 TreeSet 关于 key 有序 6、TreeMap 和 TreeSet 的关系 7、总结 1、认识 TreeMap 和 TreeSet TreeMap 和 TreeSet 是Ja…

探索SpringMVC-组件之ViewResolver

前言 ViewResolver也就是视图解析器&#xff0c;他将是我们《探索SpringMVC》系列要介绍的最后一个常用的组件。其他组件&#xff1a;MultipartResolver、LocaleResolver、ThemeResolver、RequestToViewNameTranslator、FlashMapManager&#xff0c;相对简单&#xff0c;大家可…

一个想活得简单的程序猿的2022年终总结!

前言 今年的总结相比以往来说&#xff0c;可写的太少了&#xff0c;但看到我17年开始写的年终总结&#xff0c;已定下每年写下的承诺&#xff0c;因此即便可写的不多&#xff0c;但是还是写下吧&#xff0c;毕竟又过了一年&#xff0c;总有东西会留下&#xff01; 今年事件 疫…

【Linux杂篇】Windows远程登陆Linux、Linux静态IP配置

前言 如果要长期连接Linux环境&#xff0c;就需要给Linux配置一个静态IP&#xff0c;否则可能每次连接的IP都不一样而且还很麻烦。 除此之外&#xff0c;我们使用ssh远程登录的时候&#xff0c;每次都要输入密码&#xff0c;也很麻烦&#xff0c;所以建议配置ssh密钥&#xff…

执行 java -jar xxx.jar 的时候底层到底做了什么?

大家都知道我们常用的 SpringBoot 项目最终在线上运行的时候都是通过启动 java -jar xxx.jar 命令来运行的。那你有没有想过一个问题&#xff0c;那就是当我们执行 java -jar 命令后&#xff0c;到底底层做了什么就启动了我们的 SpringBoot 应用呢&#xff1f;或者说一个 Sprin…

Redis删除了大量数据后,为什么内存占用还是很高?

前言 上周刚来了个应届小师弟&#xff0c;组长说让我带着&#xff0c;周二问了我这样一个问题&#xff1a;师兄啊&#xff0c;我用top命令看了下服务器的内存占用情况&#xff0c;发现Redis内存占用严重&#xff0c;于是我就删除了大部分不用的keys&#xff0c;为什么内存占用…

文件操作【C语言】

目录 一、为什么使用文件 二、什么是文件 1、程序文件 2、数据文件 3、文件名 三、文件的打开和关闭 1、文件指针 2、文件的打开和关闭 四、文件的顺序读写 五、文件的随机读写 1、fseek 2、ftell 3、rewind 七、文件读取结束的判定 1、被错误使用的feof 1、文…

unocss原子化

文章目录1. 安装2. 配置3. Unocss预设3.1 presetUno3.2 presetAttributify3.3 presetIcons了解什么是UnoCSS请看&#xff1a;重新构想原子化CSS - 知乎 github地址&#xff1a;UnoCSS UnoCSS搜索引擎 1. 安装 npm i -D unocss2. 配置 vite.config.ts import { defineConf…

分享微信抽奖小程序制作步骤_微信抽奖小程序怎么开发

各位商家在节日期间做活动的时候&#xff0c;都希望用更少的费用去或者更好的宣传和推广的效果。比较常见的就是抽奖活动小程序。无须玩家下载&#xff0c;通过微信扫码或者指定入口就可以参与。方便&#xff0c;效果又好。那么,性价比高的抽奖活动小程序怎么做&#xff1f; 来…

LabVIEW使用VI脚本重新排列对象

LabVIEW使用VI脚本重新排列对象VI脚本可用来重新排列前面板和程序框图的对象。该教程以程序框图对象重新排列为例。按照下列步骤&#xff0c;使用VI脚本重新排列程序框图对象。创建VI前&#xff0c;需先了解VI脚本的基本内容。必须启用VI脚本&#xff0c;才能显示VI脚本选板&am…

solr-cloud集群

Zookeeper集群搭建完成&#xff0c;下面开始构建solr-cloud从复制四个tomcat实例开始将配置好的单机版solr复制到tomcat实例下修改tomcat端口号vim tomcat01/conf/server.xmlvim tomcat02 /conf/server.xml使用配置好的单机版solrhome关联solr和solrhomevim tomcat01/webapps/s…

数据库系统概念 | 第三章:SQL介绍

文章目录&#x1f4da;SQL语言概览&#x1f4da;SQL数据定义&#x1f407;基本数据类型&#x1f407;基本模式定义&#x1f955;create table&#x1f955;create domain&#x1f955;drop table&#x1f955;delete table&#x1f955;alter table&#x1f4da;SQL查询的基本结…

Transformer模型详解

1. 前言 transformer结构是google在2017年的Attention Is All You Need论文中提出&#xff0c;在NLP的多个任务上取得了非常好的效果&#xff0c;可以说目前NLP发展都离不开transformer。最大特点是抛弃了传统的CNN和RNN&#xff0c;整个网络结构完全是由Attention机制组成。 …

VESC操作入门——控制霍尔电机、无感电机和AS5047P

目录一、设备说明二、VESC4驱动霍尔电机2.1、硬件准备2.2、硬件连接2.3、打开软件2.4、连接2.5、校准电机2.6、主界面操作三、VESC4驱动无感电机3.1、硬件准备3.2、硬件连接3.3、打开软件3.4、校准电机四、VESC4驱动AS5047P4.1、软硬件修改4.2、硬件准备4.3、硬件连接4.4、校准…

Win32解决透明字体改变时重叠的问题,GetClientRect与GetWindowRect的使用

透明字体,改变时发生文本重叠,解决办法是刷新窗体局部区域,该区域是文本或者按钮等控件的区域 Win32 API中使用InvalidateRect函数使指定区域失效,意味着要刷新该区域,再用UpdateWindow函数强迫窗体立即刷新 RECT rc; ... InvalidateRect(hWnd,&rc,true); UpdateWind…

Python操作文件及其内容的常用方式

Python操作文件及其内容的常用方式 文章目录Python操作文件及其内容的常用方式1&#xff1a;修改文件名1.1&#xff1a;修改指定文件名1.2&#xff1a;修改目录下的所有文件的文件名2&#xff1a;读取文件2.1&#xff1a;读取文件内容2.1.1&#xff1a;按行读取2.1.2&#xff1…

[Arduino]环境安装与配置

最近着迷与Arduio&#xff0c;可以连接控制各种器件帮助人类降低负担&#xff0c;如室内外温度动态采集、声控灯、自动给绿植浇水等各种应用&#xff0c;感觉挺有意思&#xff1b;随着最近两年物联网的推广及“万物互联”的普及&#xff0c;个人觉得物联网还是有点花样的&#…

认证授权功能分析

1 模块需求分析 1.1 什么是认证授权 截至目前&#xff0c;项目已经完成了课程发布功能&#xff0c;课程发布后用户通过在线学习页面点播视频进行学习。如何去记录学生的学习过程呢&#xff1f;要想掌握学生的学习情况就需要知道用户的身份信息&#xff0c;记录哪个用户在什么…