算法精讲:分享一道值得分享的算法题

分享一道leetcode上的题,当然,居然不是放在刷题贴里来讲,意味着分享的这道题不仅仅是教你怎么来解决,更重要的是这道题引发出来的一些解题技巧或许可以用在其他地方,下面我们来看看这道题的描述。

问题描述

给定一个未排序的整数数组,找出其中没有出现的最小的正整数。

示例 1:输入: [1,2,0]
输出: 3
示例 2:输入: [3,4,-1,1]
输出: 2
示例 3:输入: [7,8,9,11,12]
输出: 1
复制代码

说明: 你的算法的时间复杂度应为O(n),并且只能使用常数级别的空间。

解答

这道题在 leetcode 的定位是困难级别,或许你可以尝试自己做一下,然后再来看我的解答,下面面我一步步来分析,秒杀的大佬请忽略.....

对于一道题,如果不能第一时间想到最优方案时,我觉得可以先不用那么严格,可以先采用暴力的方法求解出来,然后再来一步步优化。像这道题,我想,如果可以你要先用快速排序先把他们排序,然后在再来求解的话,那是相当容易的,不过 O(nlogn) 的时间复杂度太高,其实我们可以先牺牲下我们的空间复杂度,让保证我们的时间复杂度为 O(n),之后再来慢慢优化我们的空间复杂度。

方法一:采用集合

我们知道,如果数组的长度为 n,那么我们要找的目标数一定是出于 1~n+1 之间的,我们可以先把我们数组里的所有数映射到集合里,然后我们从 1~n 开始遍历判断,看看哪个数是没有在集合的,如果不存在的话,那么这个数便是我们要找的数了。如果 1~n 都存在,那我们要找的数就是 n+1 了。

不过这里需要注意的是,在把数组里面的数存进集合的时候,对于 小于 1 或者大于 n 的数,我们是不需要存进集合里的,因为他们不会对结果造成影响,这也算是一种优化吧。光说还不行,还得会写代码,代码如下:

    public int firstMissingPositive(int[] nums) {Set<Integer> set = new HashSet<>();int n = nums.length;for (int i = 0; i < n; i++) {if (nums[i] >= 1 && nums[i] <= n) {set.add(nums[i]);}}for (int i = 1; i <= n; i++) {if (!set.contains(i)) {return  i;}}return n + 1;}
复制代码

采用 bitmap

方法一的空间复杂度在最块的情况下是 O(n),不知道大家还记不记得位算法,其实我们是可以利用位算法来继续优化我们的空间的,如果不知道位算法的可以看我直接写的一篇文章:

1、什么是bitmap算法。

2、自己用代码实现bitmap算法;

通过采用位算法,我们我们把空间复杂度减少8倍,即从 O(n) -> O(n/32),但其实 O(n/32) 任然还算 O(n),不过,在实际运行的时候,它是确实能够让我们运行的更快的,在 Java 中,已经有自带的支持位算法的类了,即 bitSet,如果你没学过这个类,我相信你也是能一眼看懂的,代码如下:

    public int firstMissingPositive2(int[] nums) {BitSet bitSet = new BitSet();int n = nums.length;for (int i = 0; i < n; i++) {if (nums[i] >= 1 && nums[i] <= n) {bitSet.set(nums[i]);}}for (int i = 1; i <= n; i++) {if (!bitSet.get(i)) {return  i;}}return n + 1;}
复制代码

方法3:最终版本

如果这个数组是有序的,那就好办了,但是如果我们要把它进行排序的话,又得需要 O(nlogn) 的时间复杂度,那我们有没有啥办法把它进行排序,然后时间复杂度又不需要那么高呢?

答是可以,刚才我们说过,对于那些小于 1 或者大于 n 的数,我们是其实是可以不理的,居然我们,我们需要处理的这些数,他们都是处于 1~n 之间的,那要你给这些处于 1~n 之间的数排序,并且重复的元素我们也是可以忽略掉的,记录一个就可以了,那么你能不能在 O(n) 时间复杂度排序好呢?

不知道大家是否还记得我之间写过的下标法

一些常用的算法技巧总结。

或者是否还记得计数排序?(计数排序其实就是下标法的一个应用了)

不过学过计数排序的朋友可能都知道,计数排序是需要我们开一个新的数组的,不过我们这里不需要,这道题我们可以这样做:例如对于 nums[i],我们可以把它放到数组下标位 nums[i] - 1 的位置上,这样子一处理的话,所有 1<=nums[i]<=n 的数,就都是是处于相对有序的位置了。注意,我指的是相对,也就是说对于 1-n 这些数而言,其他 小于 1 或者大于 n 的我们不理的。例如对于这个数组 nums[] = {4, 1, -1, 3, 7}。

让 nums[i] 放到数组下标为 nums[i-1]的位置,并且对于那些 nums[i]<=0 或 nums > n的数,我们是可以不用理的,所以过程如下:从下标为 0 开始向右遍历

1、把 4 放在下标为 3 的位置,为了不让下标为 3 的数丢失,把下标为 3 的数与 4进行交换。

2、此时我们还不能向右移动来处下标为1的数,因为我们当前位置的3还不是处于有序的位置,还得继续处理,所以把 3 与下标为 2 的数交换

3、当前位置额数为 -1,不理它,前进到下标为 1 的位置,把 1 与下标为 0的数交换

4、当前位置额数为 -1,不理它,前进到下标为 2 的位置,此时的 3 处于有序的位置,不理它继续前进,4也是处于有序的位置,继续前进。

5、此时的 7 > n,不理它,继续前进。

遍历完成,此时,那些处于 1~n的数都是处于有序的位置了,对于那些小于1或者大于n的,我们忽略它假装没看到就是了

这里还有个要注意的地方,就是 nums[i] 与下标为 nums[i]-1的数如果相等的话也是不需要交换的。

接下来,我们再次从下标 0 到下标 n-1 遍历这个数组,如果遇到 nums[i] != i + 1 的数,,那么这个时候我们就找到目标数了,即 i + 1。

好吧,我觉得我讲的有点啰嗦了,还一步步话题展现过程给你们看,连我自己都感觉有点啰嗦了,大佬勿喷哈。最后代码如下:

    public int firstMissingPositive(int[] nums) {if(nums == null || nums.length < 1)return 1;int n = nums.length;for(int i = 0; i < n; i++){// 这里还有个要注意的地方,就是 nums[i] 与下标为 nums[i]-1的数如果相等的话// 也是不需要交换的。while(nums[i] >= 1 && nums[i] <= n && nums[i] != i + 1 && nums[i] != nums[nums[i]-1] ){// 和下标为 nums[i] - 1的数进行交换int tmp = nums[i];nums[i] = nums[tmp - 1];nums[tmp - 1] = tmp;}}for(int i = 0; i < n; i++){if(nums[i] != i + 1){return i + 1;}}return n + 1;}
复制代码

这道题我觉得还是由挺多值得学习的地方的,例如它通过这道原地交换的方法,使指定范围内的数组有序了。

还有就是这种通过数组下标来解决问题的方法也是一种常用的技巧,例如给你一副牌,让你打乱顺序,之后分发给4个人,也是可以采用这种方法的,详情可以看这道题:什么是洗牌算法。

最后推广下我的公众号:苦逼的码农:公众号里面已经有100多篇原创文件,也分享了很多实用工具,海量视频资源、电子书资源,关注自提。点击扫码关注哦。 戳我即可关注,

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

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

相关文章

正几边形可以实现无缝拼接?

正n边形内角为 (n-2)*180/n &#xff0c;要保证可以无缝拼接&#xff0c;就是一个圆可以被整数个n边形内角拼接&#xff0c;即 360k*(n-2)*180/n > 2nk(n-2)。&#xff08;摘自http://blog.csdn.net/ray58750034/article/details/1365813&#xff09; 以下代码表明&#xff…

React 18 Beta 来了

大家好&#xff0c;我是若川。最近组织了源码共读活动&#xff0c;感兴趣的可以加我微信 ruochuan12 参与&#xff0c;目前近3000人参与。经过「React18工作组」几个月工作&#xff0c;11月16日v18终于从Alpha版本更新到Beta版本。本文会解释&#xff1a;这次更新带来的变化对开…

osg着色语言着色_探索数字着色

osg着色语言着色Learn how to colorize icons with your NounPro subscription and Adobe Illustrator.了解如何使用NounPro订阅和Adobe Illustrator为图标着色。 For those who want to level up their black and white Noun Project icons with a splash of color, unlockin…

upc组队赛15 Supreme Number【打表】

Supreme Number题目链接 题目描述 A prime number (or a prime) is a natural number greater than 1 that cannot be formed by multiplying two smaller natural numbers. Now lets define a number N as the supreme number if and only if each number made up of an non-e…

CSS3实践之路(一):CSS3之我观

CSS 的英文全称Cascading Style Sheets&#xff0c;中文意思是级联样式表,通过设立样式表&#xff0c;可以统一地控制HMTL中各DOM元素的显示属性。级联样式表可以使人更能有效地控制网页外观。使用级联样式表&#xff0c;可以扩充精确指定网页元素位置&#xff0c;外观以及创建…

18个项目必备的JavaScript代码片段——数组篇

大家好&#xff0c;我是若川。最近组织了源码共读活动&#xff0c;感兴趣的可以加我微信 ruochuan12 参与&#xff0c;目前近3000人参与&#xff0c;0-5年工作经验的都可以参与学习。1.chunk转换二维数组将数组&#xff08;array&#xff09;拆分成多个数组&#xff0c;并将这些…

美学评价_卡美学的真正美

美学评价In collectible card games like Hearthstone, Legends of Runeterra, and Magic: The Gathering, the aesthetic of the cards is indubitably one of the greatest highlights for many, if not all players. Although the game loop is reliant on physically build…

好程序员web前端分享CSS Bug、CSS Hack和Filter学习笔记

为什么80%的码农都做不了架构师&#xff1f;>>> CSS Bug、CSS Hack和Filter学习笔记 1)CSS Bug:CSS样式在各浏览器中解析不一致的情况&#xff0c;或者说CSS样式在浏览器中不能正确显示的问题称为CSS bug. 2)CSS Hack: CSS中&#xff0c;Hack是指一种兼容CSS在不同…

as3 浅复制 深复制

基元数据类型&#xff1a;boolean、int、uint、number、string 两种复杂数据类型&#xff1a;array、object 当数组元素全部是基元数据类型时&#xff0c;即全部是值类型时&#xff0c;是没有浅复制和深复制的区别。 当数组元素全部是复杂数据类型&#xff0c;即引用类型时&…

碎片化学前端,促进技术提升,我推荐这些

大家好&#xff0c;我是若川。众所周知&#xff0c;关注公众号可以了解学习掌握技术方向&#xff0c;学习优质好文&#xff0c;落实到自己项目中。还可以结交圈内好友&#xff0c;让自己融入到积极上进的技术氛围&#xff0c;促进自己的技术提升。话不多说&#xff0c;推荐这些…

ux和ui_设计更好的结帐体验-UX / UI案例研究

ux和uiPlated Cuisine is a food ordering and delivery app for Plated Cuisine Restaurant founded and managed by Rayo Odusanya.Plated Cuisine是由Rayo Odusanya创建和管理的Plated Cuisine Restaurant的食品订购和交付应用程序。 A short background about Rayo Rayo O…

Django中ajax发送post请求,报403错误CSRF验证失败解决办法

今天学习Django框架&#xff0c;用ajax向后台发送post请求&#xff0c;直接报了403错误&#xff0c;说CSRF验证失败&#xff1b;先前用模板的话都是在里面加一个 {% csrf_token %} 就直接搞定了CSRF的问题了&#xff1b;很显然&#xff0c;用ajax发送post请求这样就白搭了&…

如何在EXCEL中添加下拉框

筛选主要是将已有列的信息以下拉框的形式显示出来 选中数据栏中的筛选按钮即可生成 如果是想添加未有信息则如下图步骤 首先&#xff0c;选择你要出现下拉的区域&#xff0c;在数据栏中的选择数据有效性 然后&#xff0c;下面对话框中&#xff0c;有效性条件中按如下设置即可&a…

每次新增页面复制粘贴?100多行源码的 element-ui 的新增组件功能教你解愁

1. 前言大家好&#xff0c;我是若川。最近组织了源码共读活动&#xff0c;感兴趣的可以点此加我微信ruochuan12 参与&#xff0c;每周大家一起学习200行左右的源码&#xff0c;共同进步。已进行三个月了&#xff0c;很多小伙伴表示收获颇丰。想学源码&#xff0c;极力推荐之前我…

原子设计_您需要了解的有关原子设计的4件事

原子设计重点 (Top highlight)Industries such as Architecture or Industrial Design have developed smart modular systems for manufacturing extremely complex objects like airplanes, ships, and skyscrapers. Inspired by this, Atomic Design was proposed as a syst…

深度学习 Caffe 初始化流程理解(数据流建立)

之前在简书的文章&#xff0c;搬迁过来 ^-^ 本文是作者原创&#xff0c;如有理解错误&#xff0c;恳请大家指出&#xff0c;如需引用&#xff0c;请注明出处。 #Caffe FeatureMap数据流的建立 ##用语解释 FeatureMap: 输入的图片信息或者经过多层处理后的图片信息。weights: 只…

C#中的Clipboard与ContextMenuStrip应用举例

今天&#xff0c;突然想起了怎样在一个文本中实现复制、剪切与粘贴的功能&#xff0c;并给这些功能添加右键的快捷方式。于是&#xff0c;就用自己的VS2008写了一个简单的小应用&#xff0c;以熟悉C#中剪贴板与快捷菜单的使用。 首先&#xff0c;我们不难发现&#xff0c;剪贴板…

控制台ui_设计下一代控制台UI

控制台ui游戏UX (GAMES UX) Yesterday’s Sony presentation showed us the final look of the PlayStation 5, as well as an impressive of next-gen games that will be released with it. What we didn’t get to see, however, is the new operating system and it’s use…

写给前端新手看的一些模块化知识

大家好&#xff0c;我是若川。最近组织了源码共读活动&#xff0c;感兴趣的可以点此加我微信ruochuan12 进群参与&#xff0c;每周大家一起学习200行左右的源码&#xff0c;共同进步。已进行三个月了&#xff0c;很多小伙伴表示收获颇丰。一、 为什么需要模块化以前没有模块化时…

重学前端学习笔记(八)--JavaScript中的原型和类

笔记说明 重学前端是程劭非&#xff08;winter&#xff09;【前手机淘宝前端负责人】在极客时间开的一个专栏&#xff0c;每天10分钟&#xff0c;重构你的前端知识体系&#xff0c;笔者主要整理学习过程的一些要点笔记以及感悟&#xff0c;完整的可以加入winter的专栏学习【原文…