假设我有连续的整数范围[0, 1, 2, 4, 6]
,其中3
是第一个“缺失”的数字。我需要一个算法来找到第一个“洞”。由于范围非常大(可能包含2^32
条目),因此效率很重要。数字范围存储在磁盘上;空间效率也是一个主要问题。
什么是最佳时间和空间效率算法?
答案 0 :(得分:39)
使用二进制搜索。如果一系列数字没有空洞,那么范围的结束和开始之间的差异也将是该范围内的条目数。
因此,您可以从整个数字列表开始,根据前半部分是否有差距,切断前半部分或后半部分。最终你会来到一个有两个条目的范围,中间有一个洞。
时间复杂度为O(log N)
。与线性扫描相比,最差情况为O(N)
。
答案 1 :(得分:5)
基于上面@phs建议的方法,这里有C代码:
#include <stdio.h>
int find_missing_number(int arr[], int len) {
int first, middle, last;
first = 0;
last = len - 1;
middle = (first + last)/2;
while (first < last) {
if ((arr[middle] - arr[first]) != (middle - first)) {
/* there is a hole in the first half */
if ((middle - first) == 1 && (arr[middle] - arr[first] > 1)) {
return (arr[middle] - 1);
}
last = middle;
} else if ((arr[last] - arr[middle]) != (last - middle)) {
/* there is a hole in the second half */
if ((last - middle) == 1 && (arr[last] - arr[middle] > 1)) {
return (arr[middle] + 1);
}
first = middle;
} else {
/* there is no hole */
return -1;
}
middle = (first + last)/2;
}
/* there is no hole */
return -1;
}
int main() {
int arr[] = {3, 5, 1};
printf("%d", find_missing_number(arr, sizeof arr/(sizeof arr[0]))); /* prints 4 */
return 0;
}
答案 2 :(得分:3)
由于从0到 n - 1的数字按数组排序,因此第一个数字应与其索引相同。也就是说,数字0位于索引为0的单元格中,数字1位于索引为1的单元格中,依此类推。如果缺少的数字表示为 m 。小于 m 的数字位于索引与值相同的单元格中。
数字 m + 1位于索引 m 的单元格中,数字 m + 2位于具有索引的单元格 m + 1,依此类推。我们可以看到,缺少的数字 m 是第一个单元格,其值与其值不同。
因此,需要在数组中搜索以找到其值与其值不相同的第一个单元格。由于数组已经排序,我们可以在O(lg n )时间内根据二进制搜索算法找到它,如下所示:
int getOnceNumber_sorted(int[] numbers)
{
int length = numbers.length
int left = 0;
int right = length - 1;
while(left <= right)
{
int middle = (right + left) >> 1;
if(numbers[middle] != middle)
{
if(middle == 0 || numbers[middle - 1] == middle - 1)
return middle;
right = middle - 1;
}
else
left = middle + 1;
}
return -1;
}
此解决方案来自我的博客:http://codercareer.blogspot.com/2013/02/no-37-missing-number-in-array.html。
答案 3 :(得分:3)
基于@phs提供的算法
int findFirstMissing(int array[], int start , int end){
if(end<=start+1){
return start+1;
}
else{
int mid = start + (end-start)/2;
if((array[mid] - array[start]) != (mid-start))
return findFirstMissing(array, start, mid);
else
return findFirstMissing(array, mid+1, end);
}
}
答案 4 :(得分:2)
以下是我的解决方案,我认为这很简单,避免了过多的令人困惑的if语句。当你从0开始或者涉及负数时它也有效!假设客户拥有数字数组,其复杂度为 O(lg(n))时间 O(1)空间(否则它为O(n)强>)。
int missingNumber(int a[], int size) {
int lo = 0;
int hi = size - 1;
// TODO: Use this if we need to ensure we start at 0!
//if(a[0] != 0) { return 0; }
// All elements present? If so, return next largest number.
if((hi-lo) == (a[hi]-a[lo])) { return a[hi]+1; }
// While 2 or more elements to left to consider...
while((hi-lo) >= 2) {
int mid = (lo + hi) / 2;
if((mid-lo) != (a[mid]-a[lo])) { // Explore left-hand side
hi = mid;
} else { // Explore right hand side
lo = mid + 1;
}
}
// Return missing value from the two candidates remaining...
return (lo == (a[lo]-a[0])) ? hi + a[0] : lo + a[0];
}
int a[] = {0}; // Returns: 1
int a[] = {1}; // Returns: 2
int a[] = {0, 1}; // Returns: 2
int a[] = {1, 2}; // Returns: 3
int a[] = {0, 2}; // Returns: 1
int a[] = {0, 2, 3, 4}; // Returns: 1
int a[] = {0, 1, 2, 4}; // Returns: 3
int a[] = {0, 1, 2, 4, 5, 6, 7, 8, 9}; // Returns: 3
int a[] = {2, 3, 5, 6, 7, 8, 9}; // Returns: 4
int a[] = {2, 3, 4, 5, 6, 8, 9}; // Returns: 7
int a[] = {-3, -2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; // Returns: -1
int a[] = {-3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; // Returns: 10
一般程序是:
请注意,算法的假设是:
答案 5 :(得分:1)
您考虑过run-length encoding吗?也就是说,您编码第一个数字以及连续跟随它的数字的数量。您不仅可以通过这种方式非常有效地表示使用的数字,第一个洞将位于第一个游程编码段的末尾。
用你的例子说明:
[0, 1, 2, 4, 6]
将编码为:
[0:3, 4:1, 6:1]
其中x:y表示连续从x开始连续编号的一组数字。这立即告诉我们第一个缺口位于第3位。但是,请注意,当分配的地址聚集在一起时,这将更有效,而不是随机分散在整个范围内。
答案 6 :(得分:0)
如果列表已排序,我会遍历列表并执行类似Python代码的操作:
missing = []
check = 0
for n in numbers:
if n > check:
# all the numbers in [check, n) were not present
missing += range(check, n)
check = n + 1
# now we account for any missing numbers after the last element of numbers
if check < MAX:
missing += range(check, MAX + 1)
如果缺少大量数字,您可能需要使用@ Nathan的missing
列表的游程编码建议。
答案 7 :(得分:0)
Array: [1,2,3,4,5,6,8,9]
Index: [0,1,2,3,4,5,6,7]
int findMissingEmementIndex(int a[], int start, int end)
{
int mid = (start + end)/2;
if( Math.abs(a[mid] - a[start]) != Math.abs(mid - start) ){
if( Math.abs(mid - start) == 1 && Math.abs(a[mid] - a[start])!=1 ){
return start +1;
}
else{
return findMissingElmementIndex(a,start,mid);
}
}
else if( a[mid] - a[end] != end - start){
if( Math.abs(end - mid) ==1 && Math.abs(a[end] - a[mid])!=1 ){
return mid +1;
}
else{
return findMissingElmementIndex(a,mid,end);
}
}
else{
return No_Problem;
}
}
答案 8 :(得分:0)
我有一个算法可以在排序列表中找到丢失的号码。它的复杂性是logN。
public int execute2(int[] array) {
int diff = Math.min(array[1]-array[0], array[2]-array[1]);
int min = 0, max = arr.length-1;
boolean missingNum = true;
while(min<max) {
int mid = (min + max) >>> 1;
int leftDiff = array[mid] - array[min];
if(leftDiff > diff * (mid - min)) {
if(mid-min == 1)
return (array[mid] + array[min])/2;
max = mid;
missingNum = false;
continue;
}
int rightDiff = array[max] - array[mid];
if(rightDiff > diff * (max - mid)) {
if(max-mid == 1)
return (array[max] + array[mid])/2;
min = mid;
missingNum = false;
continue;
}
if(missingNum)
break;
}
return -1;
}
答案 9 :(得分:0)
基于@phs提供的算法
public class Solution {
public int missing(int[] array) {
// write your solution here
if(array == null){
return -1;
}
if (array.length == 0) {
return 1;
}
int left = 0;
int right = array.length -1;
while (left < right - 1) {
int mid = left + (right - left) / 2;
if (array[mid] - array[left] != mid - left) { //there is gap in [left, mid]
right = mid;
}else if (array[right] - array[mid] != right - mid) { //there is gap in [mid, right]
left = mid;
}else{ //there is no gapin [left, right], which means the missing num is the at 0 and N
return array[0] == 1 ? array.length + 1 : 1 ;
}
}
if (array[right] - array[left] == 2){ //missing number is between array[left] and array[right]
return left + 2;
}else{
return array[0] == 1 ? -1 : 1; //when ther is only one element in array
}
}
}
答案 10 :(得分:0)
这是一个采访问题。我们将有一个包含多个缺失数字的数组,我们将把所有缺少的数字放在ArrayList中。
public class Test4 {
public static void main(String[] args) {
int[] a = { 1, 3, 5, 7, 10 };
List<Integer> list = new ArrayList<>();
int start = 0;
for (int i = 0; i < a.length; i++) {
int ch = a[i];
if (start == ch) {
start++;
} else {
list.add(start);
start++;
i--; // a must do.
} // else
} // for
System.out.println(list);
}
}
答案 11 :(得分:0)
功能编程解决方案(Scala)
懒惰评估
def gapFinder(sortedList: List[Int], start: Int = 0): Int = {
def withGuards: Stream[Int] =
(start - 1) +: sortedList.toStream :+ (sortedList.last + 2)
if (sortedList.isEmpty) start
else withGuards.sliding(2)
.dropWhile { p => p.head + 1 >= p.last }.next()
.headOption.getOrElse(start) + 1
} // 8-line solution
// Tests
assert(gapFinder(List()) == 0)
assert(gapFinder(List[Int](0)) == 1)
assert(gapFinder(List[Int](1)) == 0)
assert(gapFinder(List[Int](2)) == 0)
assert(gapFinder(List[Int](0, 1, 2)) == 3)
assert(gapFinder(List[Int](0, 2, 4)) == 1)
assert(gapFinder(List[Int](0, 1, 2, 4)) == 3)
assert(gapFinder(List[Int](0, 1, 2, 4, 5)) == 3)
答案 12 :(得分:0)
using System;
using System.Collections.Generic;
using System.Text;
namespace Test
{
public class Counter
{
private int _count;
private string _name;
public Counter(string name)
{
**this._name = name;**
**this._count = 0;**
}
public void Increment()
{
**this._count++;**
}
public void Reset()
{
**this._count = 0;**
}
public string Name
{
get
{
return _name;
}
set
{
_name = value;
}
}
public int Value
{
get
{
return _count;
}
set
{
_count = value;
}
}
}
}
答案 13 :(得分:0)
我一直在寻找一种超级简单的方法来在 javascript 中找到具有最大潜在值的排序数组中的第一个缺失数字,并且不必太担心效率,因为我不打算使用更长的列表最多10-20个项目。这是我想出的递归函数:
function findFirstMissingNumber(sortedList, index, x, maxAllowedValue){
if(sortedList[index] == x && x < maxAllowedValue){
return findFirstMissingNumber(sortedList, (index+1), (x+1), maxAllowedValue);
}else{ return x; }
}
findFirstMissingNumber([3, 4, 5, 7, 8, 9], 0, 3, 10);
//expected output: 6
给它你的数组、你想要开始的索引、你期望的值以及你想要检查的最大值。
答案 14 :(得分:-1)
缺少
Number=(1/2)(n)(n+1)-(Sum of all elements in the array)
此处n
的大小为array+1
。
答案 15 :(得分:-1)
public static int findFirst(int[] arr) {
int l = -1;
int r = arr.length;
while (r - l > 1) {
int middle = (r + l) / 2;
if (arr[middle] > middle) {
r = middle;
}
l = middle;
}
return r;
}