首页 > 编程知识 正文

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

时间:2023-05-04 08:31:18 阅读:184462 作者:520

题目:

给定一个无序整型数组arr,找到数组中未出现的最小正整数。要求时间复杂度为O(N)空间复杂度为O(1)

例如:

arr=[-1,2,3,4]。返回1。

arr=[1,2,3,4]。返回5。

 

=========================================================

分析:

这道题要理解最小正整数的意思,最小的正整数就是1,所以考察的方法就是在数组中找1,然后找2,依次找下去...。直到第一个没有找到的数,这个数就是未出现的最小的正整数。但是这样的时间复杂度很大,达到了O(n2)。

 

先看一个时空复杂度均为O(n)的方案,思路如下:

         新建一个和原数组大小一致的新数组,通过遍历原数组将其中每个元素e(忽略掉小于1或大于数组长度的元素)填充到新数组中[e-1]位置上。之后遍历新数组就可找到目标,这个遍历可能会遇到两种情况,一般情况下,上一步的操作总有被忽略的元素,每忽略一个数,新数组中就会少填充一个正整数,如{-1,1,2,5,6}>>{1,2,0,0,5},这种情况要找的数就是第一个值为0的元素的下标+1极端情况下,上一步的操作没有被忽略的元素,如{3,2,1,5,4}>>{1,2,3,4,5},这种情况要找的数就是length+1;

         为什么开辟的新数组大小要和原数组大小一致?这是为了确保在极端情况下能够容纳下由原数组中元素组成的从1开始的最长连续整数序列。

         为什么要忽略掉大于数组长度的元素?这是因为如果存在这样的数X,剩下的小于length个元素不可能组成1~length的连续整数序列,则X更不可能在连续序列中,就没必要维护它了。

相应的代码实现如下:

1 @org.junit.Test 2 public void test() { 3 System.out.println("结果:" + func1(new int[] { -1, 5, 1, 6, 2 })); 4 System.out.println("结果:" + func1(new int[] { 3, 2, 1, 5, 4 })); 5 }/* out: 6 * [-1, 5, 1, 6, 2] >> 7 * [1, 2, 0, 0, 5] 8 * 结果:3 9 * [3, 2, 1, 5, 4] >> 10 * [1, 2, 3, 4, 5]11 * 结果:612 */13 14 public int func1(int[] arr) {15 int[] newArr = new int[arr.length];16 for (int e : arr) {17 if (e < 1 || e > arr.length) {18 continue;19 }20 newArr[e - 1] = e;21 }22 System.out.println(Arrays.toString(arr) + " >> ");23 System.out.println(Arrays.toString(newArr));24 25 for (int i = 0; i < newArr.length; i++) {26 if (newArr[i] == 0) {27 return i + 1;28 }29 }30 return arr.length + 1;31 } View Code

 

再看改进方案,减小空间复杂度为O(1),代码如下:

1 @org.junit.Test 2 public void test2() { 3 System.out.println(funcFinal(new int[]{-1,5,1,6,2})); 4 System.out.println(funcFinal(new int[]{3,2,1,5,4})); 5 }/* out: 6 * 原数组:[-1, 5, 1, 6, 2] 7 * 处理后:[1, 2, 1, 6, 2] 8 * 结果:3 9 * 原数组:[3, 2, 1, 5, 4]10 * 处理后:[1, 2, 3, 4, 5]11 * 结果:612 */13 14 public int funcFinal(int[] arr) {15 System.out.println("原数组:" + Arrays.toString(arr));16 /* 17 * right是一个边界值,表示用数组中元素组成的从1开始的连续整数序列中可能的最大值(初始等于数组长度)。18 * 处理数组过程中如果遇到比right大的数,就表示该数不合法,应该被丢掉(代码中还处理了其它表示数不合法的情况)。19 * >> 随着数组元素被处理,每遇到一个不合法的元素,就应将right减1。20 */21 int right = arr.length;22 /*23 * 索引left(初始为0),left将数组分成两部分。24 * [0,left)是处理完成的部分,其中每个元素都满足a[i]=i+1;25 * [left,right]是待处理部分。26 * >> 随着数组元素被处理,left会逐渐向右移动。27 */28 int left = 0; 29 30 while (left + 1 <= right) { // 正在处理的元素的值(left+1) <= 边界值31 // 分支1、arr[left]在理想的位置32 // 则处理完成部分长度加1,然后继续处理未完成部分的下一个待处理元素33 if (arr[left] == left + 1) { 34 left++; 35 } 36 // 分支2、arr[left]是不合法的数据37 // 则先将right减1,然后丢掉不合法的数并将待处理部分最后一个元素填充到left位置继续处理38 else if (arr[left] < left + 1 || arr[left] > right) {39 right--;40 arr[left] = arr[right];41 } 42 // 分支3、arr[left]合法,但是没有在理想的位置上43 // 则需要交换arr[left]与其理想位置上元素,然后继续处理交换后left位置处的元素44 // 求理想位置p的索引:p+1 = arr[left] >> p = arr[left]-145 else { 46 // 如果要交换的两个元素相同,也算当前处理的元素arr[left]不合法,进行与分支2一样的处理47 if(arr[left] == arr[arr[left] - 1]) {48 right--;49 arr[left] = arr[right];50 } else {51 swap(arr, left, arr[left] - 1);52 }53 }54 }55 System.out.println("处理后:" + Arrays.toString(arr));56 return left + 1;57 }58 private void swap(int[] a, int i, int j) {59 int temp = a[i];60 a[i] = a[j];61 a[j] = temp;62 } View Code

 


  

 

转载于:https://www.cnblogs.com/apeway/p/10764597.html

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。