【问题标题】:Is there any way to compare each element in an array using a single loop?有没有办法使用单个循环比较数组中的每个元素?
【发布时间】:2019-04-14 11:05:23
【问题描述】:

我需要打印一个 int 数组的任意两个元素之间的最小差异。

数组A的每个元素都小于等于它的长度。

1 <= A[i] <= A.length;

我已经在 J​​ava 中尝试了以下给出的方法 - 但是当给定数组大小 ~10^5 时,这需要超过 1 秒才能找到结果。

我认为这可能是一种幼稚的做法。有什么办法可以进一步优化吗? 可以在O(n)时间复杂度内完成吗?

static int findResult(int[] arr)
        {
                 int max =  Integer.MAX_VALUE;
                 HashSet<Integer> hs = new HashSet<Integer>();
                 for(int obj : arr)
                 {
                     hs.add(obj);
                 }
                if(hs.size()==1 )
                {
                    return 0;             // if all elements are same
                }
                 for(int i=0; i<arr.length; i++)
                 {  
                     for(int j=i+1; j<arr.length; j++)
                     {
                         int value = Math.abs(a[i]-a[j]);
                         if(value<max)
                         {
                            max = value;
                         }

                      }         
                }
        return max;  // returns the smallest positive difference
    }

【问题讨论】:

  • 您可以对数组进行排序,然后寻找最小的差异,这总是在当前项和下一项之间。
  • 它在少数情况下会起作用。像 1 2 5 7 1. 排序 - 1 1 2 5 7. 但会失败(最坏情况) - 5 2 3 1 5. 排序 -1 2 3 5 5. 无论如何谢谢 :)
  • 不,无论如何,从那以后你迭代“连续”元素,每次都计算差异。

标签: algorithm optimization array-algorithms


【解决方案1】:

1≤xi≤nO(n)

因为对于每个 xi 都满足 1≤xi≤n,所以它认为,由于鸽巢原理,要么所有值都恰好存在一次,要么一个值存在两次或更多次

在前一种情况下,差异是1(对于大于 1 个元素的数组),在后一种情况下,结果是0,因为那时有两个项目完全相等。

因此,我们可以遍历数组并跟踪数字。如果一个数字已经存在一次,我们返回0,否则,我们返回1,如:

// only if it holds that for all i, 1 <= arr[i] <= arr.length
static int findResult(int[] arr) {
    bool[] found = new bool[arr.length]
    for(int i = 0; i < arr.length; i++) {
        if(found[arr[i]-1]) {
            return 0;
        } else {
            found[arr[i]-1] = true;
        }
    }
    return 1;
}

对于满足条件的随机数组有n个元素,在n!/nn种情况下,会返回@ 987654327@,在其他情况下,它将返回0,因此随机输入的平均值为 n!/nn。随着 n 变大,几乎不可能没有“碰撞”,因此,作为@YvesDaoust says0 的近似值很可能。

没有1≤xi≤nO(n log n)

如果我们放弃约束,我们可以先对数组进行排序,在这种情况下,我们会遍历连续的元素:

static int findResult(int[] arr) {
    Arrays.sort(arr);
    int dmin = arr[1] - arr[0];
    for(int i = 2; i < arr.length; i++) {
        int d = arr[i] - arr[i-1];
        if(d < dmin) {
            if(d == 0) {
                return 0;
            }
            dmin = d;
        }
    }
    return dmin;
}

【讨论】:

  • 在 O(1) 中可能有一个近似解:return 0。这在绝大多数情况下都是正确的;-)
  • 第一个就可以了。因为有那个约束(1
  • 第二种方法中的1个微优化。当您知道它是0 时,您返回dmin
  • @WillemVanOnsem 也许我错过了什么,但第一个 findResult 代码不应该检查 if 语句:if (found[arr[i]]) return 0 else found[arr[i]] = true? (而不是found[i-1]
  • @DavidWinder:我弄错了,应该是found[arr[i]-1],谢谢:)
【解决方案2】:

我假设A 是整数,否则条件1 &lt;= A[i] &lt;= A.len 没有相关性。

然后有一个O(n)使用直方图的解决方案。

  1. 声明一个大小为A.length的计数器数组;

  2. 统计A的元素的重数;

  3. 扫描此直方图以找到最近的非空箱。


请注意,此解决方案假定仅考虑非零差异。如果零差异也很重要,那么威廉的答案会更好。

【讨论】:

    【解决方案3】:

    (补充以上答案)

    如果您需要使用 O(1) 额外空间来执行此操作,您可以在输入序列 A 上使用以下技巧:

    for a in A[1...n]:        // a is an element in A; A is 1-indexed; 1 <= a <= n
        if M < A[a % M]:
            return 0
        A[a % M] += M
    return 1
    

    这里 M (> n) 使得 M + n 不会溢出。这个技巧可以很容易地修改为使用小于 -n 的 M。

    注意事项:

    1. 需要随机(即 O(1))访问 A 的元素。
    2. 缓存不友好。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-01-16
      • 1970-01-01
      • 1970-01-01
      • 2014-12-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多