我同事在面试中提出了一个有趣的问题:
假设你被给予一个未排序的、非常长的无符号64位整数列表。你如何找到列表中不存在的最小的非负整数?
后续问题:既然已经提出了排序的显而易见的解决方案,你能否比O(n log n)更快地完成任务?
后续问题:你的算法必须在计算机上运行,例如1GB的内存。
澄清:这个列表在RAM中,尽管它可能占用大量内存。你预先知道列表的大小N。
我同事在面试中提出了一个有趣的问题:
假设你被给予一个未排序的、非常长的无符号64位整数列表。你如何找到列表中不存在的最小的非负整数?
后续问题:既然已经提出了排序的显而易见的解决方案,你能否比O(n log n)更快地完成任务?
后续问题:你的算法必须在计算机上运行,例如1GB的内存。
澄清:这个列表在RAM中,尽管它可能占用大量内存。你预先知道列表的大小N。
以下是我用Java编写的答案:
基本思路: 1- 循环遍历数组,丢弃重复的正数、零和负数,同时求出其余数的总和、最大正数,并将唯一的正数存储在Map中。
2- 计算最大值乘以(最大值加1)除以2的总和。
3- 计算步骤1和步骤2计算出的总和之间的差异。
4- 再次循环从1到[总和差异,最大值]的最小值,并返回不在步骤1中填充的Map中的第一个数字。
public static int solution(int[] A) {
if (A == null || A.length == 0) {
throw new IllegalArgumentException();
}
int sum = 0;
Map<Integer, Boolean> uniqueNumbers = new HashMap<Integer, Boolean>();
int max = A[0];
for (int i = 0; i < A.length; i++) {
if(A[i] < 0) {
continue;
}
if(uniqueNumbers.get(A[i]) != null) {
continue;
}
if (A[i] > max) {
max = A[i];
}
uniqueNumbers.put(A[i], true);
sum += A[i];
}
int completeSum = (max * (max + 1)) / 2;
for(int j = 1; j <= Math.min((completeSum - sum), max); j++) {
if(uniqueNumbers.get(j) == null) { //O(1)
return j;
}
}
//All negative case
if(uniqueNumbers.isEmpty()) {
return 1;
}
return 0;
}
虽然隐藏因素相当大,但您可以在O(n)时间和O(1)附加空间内完成它。这不是解决问题的实用方法,但仍然可能很有趣。
对于每个无符号64位整数(按升序排列),迭代列表直到找到目标整数或达到列表末尾。如果到达列表末尾,则目标整数是不在列表中的最小整数。如果到达64位整数的末尾,则列表中包含每个64位整数。
以下是Python函数:
def smallest_missing_uint64(source_list):
the_answer = None
target = 0L
while target < 2L**64:
target_found = False
for item in source_list:
if item == target:
target_found = True
if not target_found and the_answer is None:
the_answer = target
target += 1L
return the_answer
这个函数是故意低效的,以保持它的O(n)。特别要注意的是,即使找到答案后,该函数仍会继续检查目标整数。如果该函数在找到答案后立即返回,则外部循环运行的次数将受到答案大小的限制,而答案大小又受n的限制。这种改变会使运行时间变为O(n^2),尽管速度会更快。
int i = 0;
while ( i < Array.Length)
{
if (Array[i] == i + 1)
{
i++;
}
if (i < Array.Length)
{
if (Array[i] <= Array.Length)
{//SWap
int temp = Array[i];
int AnoTemp = Array[temp - 1];
Array[temp - 1] = temp;
Array[i] = AnoTemp;
}
else
i++;
}
}
for (int j = 0; j < Array.Length; j++)
{
if (Array[j] > Array.Length)
{
Console.WriteLine(j + 1);
j = Array.Length;
}
else
if (j == Array.Length - 1)
Console.WriteLine("Not Found !!");
}
}
(*您可以使用位移等技术来更高效地完成此操作,只需相应地改变桶的数量和大小即可。)
如果在一组数字中只有一个缺失的数字,找到缺失的数字最简单的方法是对该数列求和,然后从总和中减去列表中的每个值。最终的值就是缺失的数字。
1) 过滤负数和零
2) 排序/去重
3) 访问数组
复杂度: O(N) 或 O(N * log(N))
使用 Java8
public int solution(int[] A) {
int result = 1;
boolean found = false;
A = Arrays.stream(A).filter(x -> x > 0).sorted().distinct().toArray();
//System.out.println(Arrays.toString(A));
for (int i = 0; i < A.length; i++) {
result = i + 1;
if (result != A[i]) {
found = true;
break;
}
}
if (!found && result == A.length) {
//result is larger than max element in array
result++;
}
return result;
}
使用Python并不是最高效的方式,但是是正确的。
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
import datetime
# write your code in Python 3.6
def solution(A):
MIN = 0
MAX = 1000000
possible_results = range(MIN, MAX)
for i in possible_results:
next_value = (i + 1)
if next_value not in A:
return next_value
return 1
test_case_0 = [2, 2, 2]
test_case_1 = [1, 3, 44, 55, 6, 0, 3, 8]
test_case_2 = [-1, -22]
test_case_3 = [x for x in range(-10000, 10000)]
test_case_4 = [x for x in range(0, 100)] + [x for x in range(102, 200)]
test_case_5 = [4, 5, 6]
print("---")
a = datetime.datetime.now()
print(solution(test_case_0))
print(solution(test_case_1))
print(solution(test_case_2))
print(solution(test_case_3))
print(solution(test_case_4))
print(solution(test_case_5))
可以使用unordered_set来存储所有正数,然后我们可以从1迭代到unordered_set的长度,并查看第一个不出现的数字。
int firstMissingPositive(vector<int>& nums) {
unordered_set<int> fre;
// storing each positive number in a hash.
for(int i = 0; i < nums.size(); i +=1)
{
if(nums[i] > 0)
fre.insert(nums[i]);
}
int i = 1;
// Iterating from 1 to size of the set and checking
// for the occurrence of 'i'
for(auto it = fre.begin(); it != fre.end(); ++it)
{
if(fre.find(i) == fre.end())
return i;
i +=1;
}
return i;
}
def solution(A):
index = 0
target = []
A = [x for x in A if x >=0]
if len(A) ==0:
return 1
maxi = max(A)
if maxi <= len(A):
maxi = len(A)
target = ['X' for x in range(maxi+1)]
for number in A:
target[number]= number
count = 1
while count < maxi+1:
if target[count] == 'X':
return count
count +=1
return target[count-1] + 1
以上解决方案获得了100%的分数。
这里有一个Java答案,它不修改输入,并使用O(N)时间和N位加上少量的内存开销(其中N是列表的大小):
int smallestMissingValue(List<Integer> values) {
BitSet bitset = new BitSet(values.size() + 1);
for (int i : values) {
if (i >= 0 && i <= values.size()) {
bitset.set(i);
}
}
return bitset.nextClearBit(0);
}