面试热题(缺失的第一个正数)

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

请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。

输入:nums = [1,2,0]
输出:3

 尝试的路途是痛苦的,不断的尝试新方法,错何尝不是一种乐趣

  • 纯暴力(双层for循环)
 public int firstMissingPositive(int[] nums) {for (int i = 1; i <= nums.length; i++) {boolean has = false;for (int j = 0; j < nums.length; j++) {if (nums[j] == i) {has = true;break;}}if (!has) {//没有找到这个数,直接返回return i;}}return nums.length + 1;}

       第一层循环遍历[1,nums.length]的元素,第二层元素查看当前数组中是否存在,第一个不存在的就是第一个缺失的整数,这种纯暴力搜,案例肯定能过,但是时间复杂度过高,一般都会超时

  • 排序+二分查找

       二分查找的时间复杂度是O(logn)的,这种一般是不会超时,但是我们要先将数组进行排序,因为二分查找的前提条件是具有单调性

 Arrays.sort(nums);

       遍历[1,nums.length]中的元素,通过二分搜索去在排序后的数组中查找,第一次没有查到就是第一个缺失的整数

for(int i=1;i<=nums.length;i++){int res=binarySearch(nums,i);if(res==-1){return i;}}

 普通的二分搜索:

public int binarySearch(int[] nums,int target){int left=0;int right=nums.length-1;while(left<=right){int mid=left+(right-left)/2;if(nums[mid]==target){return mid;}else if(nums[mid]>target){right=mid-1;}else{left=mid+1;}}return -1;}

  • 哈希表
           利用哈希表对原数组进行一个存储,遍历[1,nums.length]中的元素,如果在set中不存在,就是第一个缺少的整数
     
     public int firstMissingPositive(int[] nums) {int len = nums.length;Set<Integer> hashSet = new HashSet<>();for (int num : nums) {hashSet.add(num);}for (int i = 1; i <= len; i++) {if (!hashSet.contains(i))return i;}return len + 1;}

  • 位图

       假设我们使用一个位图(bitmap)来表示集合,其中每一位代表一个元素是否存在于集合中。但是这种位图只适合集合数量不是太多的情况,显然这道题不满足这个条件

错误实例:

 public int firstMissingPositive(int[] nums) {int len = nums.length;int hash = 0;for (int num : nums) {if (num > 0&&num<=nums.length) {  // 忽略非正整数hash |= 1 << (num - 1);//将当前元素加入位图}}for (int i = 1; i <= len + 1; i++) {//判断当前元素是否存在于位图if ((hash & (1 << (i - 1))) == 0) {return i;}}return len + 1; }

 基本也能过个80%+,但是我们怎么才能巧妙的利用这种方式去解决这个问题呢?

public int firstMissingPositive(int[] nums) {public int firstMissingPositive(int[] nums) {int length = nums.length;int bit[] = new int[((length - 1)>>5) + 1];for (int i = 0; i < nums.length; i++) {int digit = nums[i];//数组必须在1到length之间才有效if (digit >= 1 && digit <= length) {int index = (digit - 1)>>5;//x%N  如果N是2的次数可以写成 x&(N-1)bit[index] |= (1 << ((digit - 1) & 31));}}//最后在执行一遍循环,查看对应位置的元素是否正确,如果不正确直接返回for (int i = 0; i < nums.length; i++) {if ((bit[i >>5] & (1 << (i & 31))) == 0)return i + 1;}return length + 1; }}

  • 置换

置换顾名思义就是通过不断的交换将数组中的值和索引相对应

       通过的不断的置换,对应的位置与相对应的索引进行匹配完成,再遍历原数组,第一个数值和索引不匹配的就是第一个缺失的整数

 public int firstMissingPositive(int[] nums) {if(nums==null||nums.length==0){return 0;}//置换 值和索引相匹配for(int i=0;i<nums.length;i++){while(nums[i]>=1&&nums[i]<=nums.length&&nums[nums[i]-1]!=nums[i]){int temp=nums[nums[i]-1];nums[nums[i]-1]=nums[i];nums[i]=temp;}}for(int i=0;i<nums.length;i++){if(nums[i]!=i+1){return i+1;}}return nums.length+1;}

  • 对应法

      我们可以把每个元素存放到对应的位置,比如1存放到数组的第一个位置,3存放到数组的第3个位置, 如果是非正数或者大于数组的长度的值,我们不做处理,最后在遍历一遍数组,如果位置不正确,说明这个位置没有这个数,我们就直接返回

image.png

image.png

image.png

 public int firstMissingPositive(int[] nums) {for (int i = 0; i < nums.length; i++) {//如果在指定的位置就不需要修改if (i + 1 == nums[i])continue;int x = nums[i];if (x >= 1 && x <= nums.length && x != nums[x - 1]) {swap(nums, i, x - 1);i--;//抵消上面的i++,如果交换之后就不++;}}//最后在执行一遍循环,查看对应位置的元素是否正确,如果不正确直接返回for (int i = 0; i < nums.length; i++) {if (i + 1 != nums[i])return i + 1;}return nums.length + 1;}//交换两个数的值public void swap(int[] A, int i, int j) {if (i != j) {A[i] ^= A[j];A[j] ^= A[i];A[i] ^= A[j];}}
  • 标记法

  1. 因为数组中中按道理只允许出现[1,nums.length]的数字,所以我们首先可以先对数组中的元素进行处理,将大于等于数组长度和小于等于0的元素值改为nums.length+1(这里只要不是合理区间内的值都可以)
  2. 遍历数组中的每一个元素,加假如遍历到3,因为数组中的索引是从0开始的,所以我们要把索引为2的值变为负数(负数相当于一个标志,如果一个索引的值为负数,证明该数已经出现过),如果相同的数岂不是给一个数加负号两次,负负得正,就相当于没有出现,所以我们为了避免这种情况,每次取负数的时候,都将原数字取绝对值以后再进行取反
  3. 遍历修改后的数字,碰到第一个非负数,该数对应的索引+1就是我们缺失的第一个正数(正数说明没有其值进行标记)
public int firstMissingPositive(int[] nums) {//对入参进行判断if(nums==null||nums.length==0){return 0;}//将数组中大于数组长度的和小于等于零的值进行替换for(int i=0;i<nums.length;i++){if(nums[i]<=0||nums[i]>nums.length+1){nums[i]=nums.length+1;}}//遍历每一个元素,进行映射,符号代表该索引的整数已经出现过for(int i=0;i<nums.length;i++){int num=Math.abs(nums[i]);if(num<=nums.length){nums[num-1]=-Math.abs(nums[num-1]);}
}for(int i=0;i<nums.length;i++){if(nums[i]>0){return i+1;}}return nums.length+1;}

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

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

相关文章

Termux SFTP 进行远程文件传输

文章目录 1. 安装openSSH2. 安装cpolar3. 远程SFTP连接配置4. 远程SFTP访问4. 配置固定远程连接地址 SFTP&#xff08;SSH File Transfer Protocol&#xff09;是一种基于SSH&#xff08;Secure Shell&#xff09;安全协议的文件传输协议。与FTP协议相比&#xff0c;SFTP使用了…

IOS开发-XCode14介绍与入门

IOS开发-XCode14介绍与入门 1. XCODE14的小吐槽2. XCODE的功能bar一览3. XCODE项目配置一览4. XCODE更改DEBUG/RELEASE模式5. XCODE单元测试 1. XCODE14的小吐槽 iOS开发工具一直有个毛病&#xff0c;就是新版本的开发工具的总会有一些奇奇怪怪的bug。比如在我的Mac-Pro&#…

redis的三种集群方式

redis有三种集群方式&#xff1a;主从复制&#xff0c;哨兵模式和集群。 1.主从复制 主从复制原理&#xff1a; 从服务器连接主服务器&#xff0c;发送SYNC命令&#xff1b; 主服务器接收到SYNC命名后&#xff0c;开始执行BGSAVE命令生成RDB文件并使用缓冲区记录此后执行的所…

确定产品需求边界需重点关注4个方面

产品需求需要确定边界&#xff0c;因为资源的是有限的。而没有边界的需求&#xff0c;会造成项目目标混乱&#xff0c;工期的延长&#xff0c;开发成本增加等问题。 1、定义最小业务单元 一般来说&#xff0c;产品不可能做大而全&#xff0c;需有自己专属的业务市场。从用户角度…

【数据结构】二叉树篇|超清晰图解和详解:二叉树的最近公共祖先

博主简介&#xff1a;努力学习的22级计算机科学与技术本科生一枚&#x1f338;博主主页&#xff1a; 是瑶瑶子啦每日一言&#x1f33c;: 你不能要求一片海洋&#xff0c;没有风暴&#xff0c;那不是海洋&#xff0c;是泥塘——毕淑敏 目录 一、题目二、题解三、代码 一、题目 …

升级iPhone 15 Pro Max还是等待下一代?看看我们的比较分析!

对于拥有13 Pro Max并即将结束两年合同的用户,或者任何想看看是否值得购买两年前非常好的旗舰iPhone的最新机型的人来说,分解iPhone 15 Pro Max与iPhone 13 Pro Max的差异非常重要。无论你的动机是什么,我们都会帮助你找到答案。 iPhone 15 Pro Max还没有发布,但很快我们就…

计算文本相似度

目录 Python中的difflib模块模块用法报告涉及的符号实现文本对比普通文本对比文本对比生成HTML报告 余弦相似度sklearn安装使用sklearn的余弦相似度词袋模型 Jaccard相似度编辑距离&#xff08;Levenshtein距离&#xff09;TF-IDFWord2VecDoc2VecBERT结论 Python中的difflib模块…

kriging-contour前端克里金插值

先看效果&#xff1a; 本项目在kriging-contour插件基础上进行了封装&#xff0c;增加了自定义区域插值&#xff0c;gitbub地址。

pycharm中配置conda

安装好pycharm和conda后&#xff0c;打开pycharm&#xff1a;

文档比对技术难点与使用场景

文档比对技术是一种用于比较两份文档之间差异的先进技术。具备较大的技术难点和场景价值。下面将对其技术难点和使用场景进行详细探讨。 1、技术难点&#xff1a; 文档比对技术所面临的挑战不仅复杂多样&#xff0c;而且相互关联。以下深入探讨了其中的几个主要技术难点&#…

JS_围绕圆形滑动

需求&#xff1a;滑动手势最大不能超过一个半径为50的圆形&#xff0c;超出围绕圆形边线滑动 这里只提供一个思路&#xff0c;下面代码可以运行&#xff0c;但是要使用需要改成自己的参数 <div style"width: 100%;height: 100vh;display: flex;justify-content: cente…

首个全国生态日:契约锁助力各行业节能减排、绿色低碳发展

在国家落实“双碳”、“2030年前实现碳达峰”等目标的背景下&#xff0c;电子签章、电子合同已经成为各行业组织绿色低碳发展的“新底座”&#xff0c;被广泛应用于“政务服务、就医、大学生就业、招投标、购房、购车、货运、保险销售、银行询证函等”众多高频常见的办事、办公…