用于查找不在列表中的最小非负整数的算法

时间:2009-04-27 14:23:22

标签: algorithm sorting integer

给定一个整数列表,我怎样才能最好地找到列表中的整数?

列表可能非常大,整数可能很大(即BigIntegers,而不仅仅是32位整数)。

如果它有任何区别,列表“可能”被排序,即99%的时间将被排序,但我不能依赖于总是被排序。

修改

为了澄清,给出列表{0,1,3,4,7},可接受的解决方案的例子是-2,2,8和10012,但我更愿意找到最小的,非负解决方案(即2)如果有一个算法可以找到它而无需对整个列表进行排序。

14 个答案:

答案 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)时间效率,因为您必须查看每个元素以确保您的最终选择不存在。所以问题是:

  1. 可以在O(N)中完成吗?
  2. 什么是最佳的空间效率?
  3. Chris Doggett找到max和add 1的解决方案是O(N)和空间效率(O(1)内存使用)

    如果你只想得到最好的答案,那么这是一个不同的问题。

答案 10 :(得分:1)

假设这是我想到的问题:

  

setints范围内的所有1都有n,但其中一个ints丢失了。告诉我int缺少哪一个。

使用一些简单的数学知识解决这个问题非常容易。众所周知,范围1 .. n的总和等于n(n+1) / 2。所以,让W = n(n+1) / 2Y =你集合中数字的总和。您的集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)

您需要对列表进行排序。这意味着要么知道它已经排序,要么对它进行排序。

  • 对列表进行排序。如果已知列表已排序,请跳过此步骤。 O(n lg n)
  • 删除所有重复的元素。如果元素已经保证不同,请跳过此步骤。为O(n)
  • 使用二分搜索将B设为列表中的1的位置。 O(lg n)
  • 如果列表中不包含1,则返回1.请注意,如果列表中包含从1到n的所有元素,则B + n处的元素必须为n + 1。 O(1)
  • 现在执行一个二进制搜索排序,从min = B,max =列表的结尾开始。调用枢轴P的位置。如果P处的元素大于(P-B + 1),则递推范围[min,pivot],否则递归范围(pivot,max)。继续直到min = pivot = max O(lg n)
  • 你的答案是(pivot-1处的元素)+1,除非你在列表的末尾并且(P-B + 1)= B,在这种情况下它是最后一个元素+ 1. O(1 )

如果列表已经排序并且具有不同的元素,则这非常有效。当列表只包含非负数元素或列表中不包含值1时,您可以进行乐观检查以使其更快。

答案 13 :(得分:-1)

刚刚接受采访,他们问我这个问题。使用最坏情况分析可以找到这个问题的答案。列表中存在的最小自然数的上限是长度(列表)。这是因为,给定列表长度的列表中存在的最小数字的最坏情况是列表0,1,2,3,4,5 .... length(list)-1。

因此,对于所有列表,列表中不存在的最小数字小于等于列表的长度。因此,启动列表t,其中n = length(list)+1个零。对应于列表中的每个数字i(小于等于列表的长度)标记将值1分配给t [i]。列表中第一个零的索引是列表中不存在的最小数字。并且因为,对于至少一个索引j,该列表n-1的下限