题目:
给定一个无序整型数组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 * 结果:6 12 */ 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 }