给定一个整数列表,我怎样才能最好地找到列表中不的整数?
列表可能非常大,整数可能很大(即BigIntegers,而不仅仅是32位整数)。
如果它有任何区别,列表“可能”被排序,即99%的时间将被排序,但我不能依赖于总是被排序。
修改
为了澄清,给出列表{0,1,3,4,7},可接受的解决方案的例子是-2,2,8和10012,但我更愿意找到最小的,非负解决方案(即2)如果有一个算法可以找到它而无需对整个列表进行排序。
答案 0 :(得分:6)
一种简单的方法是迭代列表以获得最高值n
,然后您知道n+1
不在列表中。
编辑:
找到最小正数未使用数字的方法是从零开始并扫描该数字的列表,重新开始并在找到数字时增加。为了提高效率,并利用列表排序的高概率,您可以将小于当前值的数字移动到列表中未使用的部分。
此方法使用列表的开头作为较小数字的存储空间,startIndex
变量会跟踪相关数字的开始位置:
public static int GetSmallest(int[] items) {
int startIndex = 0;
int result = 0;
int i = 0;
while (i < items.Length) {
if (items[i] == result) {
result++;
i = startIndex;
} else {
if (items[i] < result) {
if (i != startIndex) {
int temp = items[startIndex];
items[startIndex] = items[i];
items[i] = temp;
}
startIndex++;
}
i++;
}
}
return result;
}
我进行了性能测试,我在其中创建了从0到19999的100000个随机数的列表,这使得平均最低数字大约为150.在测试运行(每个1000个测试列表)时,该方法在未排序的列表中找到最小的数字平均值为8.2毫秒,排序列表平均值为0.32毫秒。
(我没有检查方法离开列表的状态,因为它可能会交换其中的一些项目。它至少会使列表包含相同的项目,并且当它将较小的值移到列表中时我认为对于每次搜索,它实际上应该变得更加有序。)
答案 1 :(得分:6)
如果数字没有任何限制,那么你可以进行线性搜索,找到列表中的最大值,并返回一个更大的数字。
如果数字确实有限制(例如,max + 1和min-1可能会溢出),那么您可以使用works well on partially sorted data的排序算法。然后浏览列表,找到不连续的第一对数字v_i和v_ {i + 1}。返回v_i + 1。
要获得最小的非负整数(基于问题中的编辑),您可以:
使用上述部分排序对列表进行排序。二进制搜索列表为0.从此值迭代列表,直到找到两个数字之间的“间隙”。如果到达列表末尾,则返回最后一个值+ 1。
将值插入哈希表。然后从0向上迭代,直到找到不在列表中的整数。
答案 2 :(得分:2)
除非经过排序,否则您必须逐项进行线性搜索,直到找到匹配项或到达列表末尾。如果您可以保证它已经排序,您可以始终使用BinarySearch的数组方法或只是滚动您自己的二进制搜索。
或者像杰森提到的那样总是可以选择使用Hashtable。
答案 3 :(得分:2)
“可能排序”意味着您必须将其视为完全未排序。如果你当然可以保证它已经分类,这很简单。只需查看第一个或最后一个元素,然后加1或减去1.
答案 4 :(得分:2)
我在正确性方面得到了100%性能, 您应该使用N log(N)复杂度的快速排序。 你去......
public int solution(int[] A) {
if (A != null && A.length > 0) {
quickSort(A, 0, A.length - 1);
}
int result = 1;
if (A.length == 1 && A[0] < 0) {
return result;
}
for (int i = 0; i < A.length; i++) {
if (A[i] <= 0) {
continue;
}
if (A[i] == result) {
result++;
} else if (A[i] < result) {
continue;
} else if (A[i] > result) {
return result;
}
}
return result;
}
private void quickSort(int[] numbers, int low, int high) {
int i = low, j = high;
int pivot = numbers[low + (high - low) / 2];
while (i <= j) {
while (numbers[i] < pivot) {
i++;
}
while (numbers[j] > pivot) {
j--;
}
if (i <= j) {
exchange(numbers, i, j);
i++;
j--;
}
}
// Recursion
if (low < j)
quickSort(numbers, low, j);
if (i < high)
quickSort(numbers, i, high);
}
private void exchange(int[] numbers, int i, int j) {
int temp = numbers[i];
numbers[i] = numbers[j];
numbers[j] = temp;
}
答案 5 :(得分:1)
除非您100%确定它已经排序,否则最快的算法仍然必须至少查看一次列表中的每个数字,以至少验证列表中的数字不。 / p>
答案 6 :(得分:1)
从理论上讲,找到最大值并添加1.假设您受BigInteger类型的最大值约束,请对列表进行排序(如果未排序),并查找间隙。
答案 7 :(得分:1)
您是否正在寻找on-line algorithm(因为您说输入是任意大的)?如果是这样,请查看Odds algorithm。
否则,正如已经建议的那样,对输入进行散列,搜索并打开/关闭布尔集的元素(散列索引到集合中)。
答案 8 :(得分:1)
有几种方法:
找到列表中最大的int并将其存储在x中。 x + 1不在列表中。使用min()和x-1也是如此。
当N是列表的大小时,分配一个大小为(N+31)/32
的int数组。对于列表中的每个元素,在数组索引v&31
处设置整数位v
(其中i/32
是元素的值)。忽略i/32 >= array.length
的值。现在搜索第一个数组项'!= 0xFFFFFFFF'(对于32位整数)。
答案 9 :(得分:1)
如果您不能保证它已经排序,那么您可以获得最佳的O(N)时间效率,因为您必须查看每个元素以确保您的最终选择不存在。所以问题是:
Chris Doggett找到max和add 1的解决方案是O(N)和空间效率(O(1)内存使用)
如果你只想得到最好的答案,那么这是一个不同的问题。
答案 10 :(得分:1)
假设这是我想到的问题:
set
到ints
范围内的所有1
都有n
,但其中一个ints
丢失了。告诉我int
缺少哪一个。
使用一些简单的数学知识解决这个问题非常容易。众所周知,范围1 .. n
的总和等于n(n+1) / 2
。所以,让W = n(n+1) / 2
让Y
=你集合中数字的总和。您的集X
中缺少的整数将为X = W - Y
。
注意: SO需要支持MathML
如果这不是那个问题,或者它更一般,那么其他解决方案之一可能是正确的。我真的无法从这个问题中说出来,因为它有点模糊。
编辑:好吧,自编辑以来,我可以看到我的回答是绝对错误的。有趣的数学,不过不少。
答案 11 :(得分:1)
我已经使用Linq和二进制搜索解决了这个问题。我得到了100%的全面回报。这是我的代码:
using System.Collections.Generic;
using System.Linq;
class Solution {
public int solution(int[] A) {
if (A == null) {
return 1;
} else {
if (A.Length == 0) {
return 1;
}
}
List<int> list_test = new List<int>(A);
list_test = list_test.Distinct().ToList();
list_test = list_test.Where(i => i > 0).ToList();
list_test.Sort();
if (list_test.Count == 0) {
return 1;
}
int lastValue = list_test[list_test.Count - 1];
if (lastValue <= 0) {
return 1;
}
int firstValue = list_test[0];
if (firstValue > 1) {
return 1;
}
return BinarySearchList(list_test);
}
int BinarySearchList(List<int> list) {
int returnable = 0;
int tempIndex;
int[] boundaries = new int[2] { 0, list.Count - 1 };
int testCounter = 0;
while (returnable == 0 && testCounter < 2000) {
tempIndex = (boundaries[0] + boundaries[1]) / 2;
if (tempIndex != boundaries[0]) {
if (list[tempIndex] > tempIndex + 1) {
boundaries[1] = tempIndex;
} else {
boundaries[0] = tempIndex;
}
} else {
if (list[tempIndex] > tempIndex + 1) {
returnable = tempIndex + 1;
} else {
returnable = tempIndex + 2;
}
}
testCounter++;
}
if (returnable == list[list.Count - 1]) {
returnable++;
}
return returnable;
}
}
在Large_2测试中,最长执行时间为0.08s
答案 12 :(得分:0)
您需要对列表进行排序。这意味着要么知道它已经排序,要么对它进行排序。
如果列表已经排序并且具有不同的元素,则这非常有效。当列表只包含非负数元素或列表中不包含值1时,您可以进行乐观检查以使其更快。
答案 13 :(得分:-1)
刚刚接受采访,他们问我这个问题。使用最坏情况分析可以找到这个问题的答案。列表中存在的最小自然数的上限是长度(列表)。这是因为,给定列表长度的列表中存在的最小数字的最坏情况是列表0,1,2,3,4,5 .... length(list)-1。
因此,对于所有列表,列表中不存在的最小数字小于等于列表的长度。因此,启动列表t,其中n = length(list)+1个零。对应于列表中的每个数字i(小于等于列表的长度)标记将值1分配给t [i]。列表中第一个零的索引是列表中不存在的最小数字。并且因为,对于至少一个索引j,该列表n-1的下限