题目来源 --- LeetCode
给定一个未排序的整数数组,找出其中没有出现的最小的正整数。
示例 1:
输入: [1,2,0]
输出: 3
示例 2:
输入: [3,4,-1,1]
输出: 2
示例 3:
输入: [7,8,9,11,12]
输出: 1
说明:
你的算法的时间复杂度应为O(n),并且只能使用常数级别的空间。
思路
1.创建一个 int [ ] 数组 arr [ ]
int 在java中占32位,我们用一个int的每一个位上的 1 或者 0 分别代表存在和不存在 (java.util.BitSet)
这样的话, arr [0] 这个int的32位就代表了正整数 1~32中是否存在
同理 arr [1] 代表了整数33~64是否存在
2.我们遍历输入的数组,把整数是否存在的信息保存在新建的arr [ ] 数组中
例如 输入的数组 [1, 2, 4, 5] 则在 arr [0]中值的 二进制表示为 0000 0000 0000 0000 0000 0000 0001 1011
我们可以看到 1,2,4,5位是1 其余都是0
那很明显 ,我们只要去找最靠右的0是第几位就好了 最靠右的0就代表着最小没有出现过的正整数
3.如何去取最靠右的0
位运算学的不好 , 采用的方法是 循环用1和每个位做 | 运算,如果值有变化,则代表该位是0 ...感觉有更好的方法.不过想不出来...
接下来return出现变化的位数就可以了
下面是java实现
public static int firstMissingPositive(int[] nums) {
//传入数组如果为1-32 刚好32个整数 , 我们需要多一个int来储存
int[] sign = new int[nums.length/32+1];
//循环输入数组的每个元素
for (int i:nums) {
//我们要的正整数,小于0的扔掉
//为了避免输入数组 [1,999999999999], 虽然只有两个数的数组,但是会产生较大的sign数组
//如果输入数组长度为4 , 最后的结果也不过1,2,3,4,5这5种情况
//所以大于nums.length也直接扔掉
if (i>0&&i<=nums.length){
//将数组中的值 对应位 置为1
//像临界值32也是要存在sign[0]中的
sign[(i-1)/32] |= (1<<(i%32-1));
}
}
//接下来遍历sign[] 取出最小位的0的位数
for (int i = 0; i < sign.length; i++) {
// 0xffffffff 的原码值为 -1 , 代表着内存中这个int的二进制32位全部被置为了1
//如果不等于-1 则标识这个sign[i] 中存在0
//如果等于-1,说明这32个全部存在,我们就去sign[i+1]中去找0
if (sign[i]!=-1){
int k = 0;
while (k<33){
//最多遍历32次就可以找到0
if (sign[i]!=(sign[i]|(1<<k++))){
//返回这个0代表的位数
return k+i*32;
}
}
}
}
//理论上不会到这一步
return -1;
}