java 如何在存在重复的数组中找到数百万个数字范围内的缺失数字?

o2gm4chl  于 2023-02-20  发布在  Java
关注(0)|答案(2)|浏览(106)
  • 我问这个问题与几个月前发布的this问题有关 *

目前,在我正在工作的应用程序中,我得到了一系列数字,其中数字可能丢失和重复,但以升序排列。
有两个问题。
1.如果没有重复的数字,使用提及问题的公认答案中建议的方法,找到缺失的数字是相当容易的。
1.但是,如果有重复的,这种方法就不再有效了。
我该如何解决这个问题呢?似乎没有逻辑可以解决。而且即使可以(使用循环),也不会有效率。

    • 注意:**我还搜索了一些库,但没有找到。
czq61nw1

czq61nw11#

据我所知,除了循环遍历之外,没有任何方法可以检测列表中缺失的数字。
如果您的数组已排序,则它应如下所示:

[1,2,3,3,4,6]

因此,这段代码应该完成以下工作:

int getMissingNumber(int[] numbers){
    for (int i=0; i<numbers.length -1; i++){
        int current = numbers[i];
        int next = numbers[i+1];
        if(next - current > 1){
            return current + 1;
        }
    }
    return -1;
}

除此之外,还可以选择将Array更改为Set,然后再更改回Array,然后使用前面的方法。请确保使用LinkedHashSet以保持插入顺序。但我不知道是否会更快。

h6my8fg2

h6my8fg22#

我只是想看看是否可以尽可能多地分解任务,使用Fork Join作为一个有趣的练习来更好地了解库,并将其与简单的for循环进行对比。

public class misc {
    public void getMissingNumbers(int[] numbers){
        for (int i=0; i<numbers.length -1; i++){
            int current = numbers[i];
            int next = numbers[i+1];
            if(current+1 != next){
                System.out.println("Problem! - "+current+" "+next);
            }
        }
    }
     
     public static void main(String []args){
         int[] range = IntStream.rangeClosed(1, 50_000_000).toArray();
         int index = 50000;
         range[index] =  range[index-1];  //duplicate
         index = 390;
         range[index] =  range[index-1];
         index = 500390;
         range[index] =  range[index-1];
         index = 2500390;
         range[index] =  range[index-1];
         
         ZonedDateTime now = ZonedDateTime.now();
         misc m = new misc();
         m.getMissingNumbers(range);
         System.out.printf("%s exec time: %dms\n",
                 m.getClass().getSimpleName(),
                 ChronoUnit.MILLIS.between(now, ZonedDateTime.now()));
         
         now = ZonedDateTime.now();
         ForkJoinPool forkJoinPool = ForkJoinPool.commonPool();
         breakDownRecursively bdr = new breakDownRecursively(range);
         forkJoinPool.invoke(bdr);
         System.out.printf("%s exec time: %dms\n",
                 bdr.getClass().getSimpleName(),
                 ChronoUnit.MILLIS.between(now, ZonedDateTime.now()));
     }
}

class breakDownRecursively extends RecursiveAction {
    private final int[] arr;
    private final ArrayList<Integer> arrlst = new ArrayList<>();
    
    public breakDownRecursively(int[] arr) {
        this.arr = arr;
    }
    
    public void compute() {
        int n = arr.length;
        if (arr.length < 2) return;
        int mid = arr.length / 2;

        int[] left = new int[mid];
        System.arraycopy(arr, 0, left, 0, mid);

        int[] right = new int[arr.length - mid];
        System.arraycopy(arr, mid, right, 0, arr.length - mid);

        invokeAll(new breakDownRecursively(left), new breakDownRecursively(right));
        compare(left, right);
    }
    
    private void compare(int[] left, int[] right) {
        if (left.length == 1 && right.length == 1) {
            if (left[0]+1 != right[0]) {
                //System.out.println("Problem! - "+left[0]+" "+right[0]);
            }
        }else if (left.length <= 2 && right.length <=2) {
            int size = left.length-1;
            if (left[size]+1 != right[0]) {
                System.out.println("Problem! - "+left[size]+" "+right[0]);
            }
        }else {
            return;
        }
    }
}

输出:

Problem! - 50000 50002
Problem! - 500390 500390
Problem! - 500390 500392
Problem! - 2500390 2500390
Problem! - 2500390 2500392
misc exec time: 59ms
Problem! - 390 390
Problem! - 50000 50002
Problem! - 500390 500390
Problem! - 2500390 2500390
breakDownRecursively exec time: 2320ms

我想我可能在实现fork join的过程中犯了一个错误,但是至少你应该看到for循环并没有那么糟糕。

相关问题