【问题标题】:Find a supplement to a subarray of ints in Java在 Java 中查找整数子数组的补充
【发布时间】:2012-11-02 10:58:06
【问题描述】:

让我们有int[] A = new int[1000]int[] subA = new int [300] 这样subA \in AsubAA 的子集)。如何在Java中以最快的方式找到数组A \ subA?给定数组AsubA 都已排序。

编辑:抱歉,忘了提到数组包含不同的元素,只是它们包含其他结构的索引,如矩阵行。

我正在考虑这个解决方案:

// supp is short for supplement
int[] supp = new int[A.length - subA.length];
int j = A[0], c = 0;
for (int i = 0; i < subA.lengh; i++) {
    // elegantly can be: while (j < subA[i]) supp[c++] = j++;
    while (j < subA[i]) {
        supp[c] = j;
        c++; j++;
    }
    j = subA[i] + 1;
}

目前正在测试这种方法。准备好答案后我会回来的。

【问题讨论】:

  • '\' 你的意思是“只有 A 中不在 subA 中的元素”?
  • 是的,这是数学上的差异,或者只是一组减号。
  • 这更多是关于算法而不是 Java,重新标记。

标签: java arrays algorithm sorting set


【解决方案1】:

试试这样的:

// A index
int ai = 0;
// subA index
int sai = 0;
// result array
int[] result = new int[A.length - subA.length];
// index in result array
int resi = 0;

while ai < A.length && sai < subA.length;
    // same elements - ignore
    if (A[ai] == subA[sai]) {
        ai++;
        sai++;
    // found an element in A that does not exist in subA
    } else {
        // Store element
        result[resi] = A[ai];
        resi++;
        ai++;
    }
}

// Store elements that are left in A
for (;ai < A.length; ai++, resi++) {
    result[resi] = A[ai];
}

【讨论】:

  • “while”循环不是“以最快的方式”!使用 Arrays.binarySearch() 查找第一个可能的匹配项 - 因为数组数组已排序,所以此方法有效。
  • @tigger errr 什么/为什么? binarySearch() 干什么用的?
  • @tigger 我的方法将在 O(n) 步内解决任务,这意味着只需要一条通过数组 A 的路径。它似乎相当快。我也不在数组中执行搜索,所以我几乎不需要这个功能。
  • 这段代码有正确的想法,但如果任何数组包含重复值,就会崩溃。
  • @Jochen:很确定它会跳过它们,因为它们已排序。那么我可能又错了......
【解决方案2】:

如果你说元素是有序的并且都是不同的,那么你只需要在A中找到subA的第一个元素的索引,然后使用System.arrayCopy()以最有效的方式复制数据:

    int index = Arrays.binarySearch(A, subA[0]);

    int[] diff = new int[A.length - subA.length];

    System.arraycopy(A, 0, diff, 0, index);
    System.arraycopy(A, index+subA.length, diff, index, A.length-index-subA.length);

PS。我没有检查所有的索引位置和计算,但你明白了。

【讨论】:

  • 很酷的想法,如果 subA 比 A 大得多,则使算法成为常数因子更快。但最终,您的算法是 O(n log n),而直接算法是 O(n)。如果您知道 A 和 subA 中的所有元素都是唯一的并且 subA 是 A 的子数组,那么使用 A 上的视图(请参阅我的答案),您会得到 O(1) 的算法。
  • 只有当 subA 是 A 的“子数组”(如子字符串)时才有效,例如如果 A = [1, 2, 3, 4, 5, 6] 和 subA = [3, 4, 5]。然后它将起作用。但是如果 A = [1, 2, 3, 4, 5, 6] 和 subA = [2, 6] 怎么办? subA 仍然是 A 的子集。
  • @DaveBall 为什么这个算法是 O(n log n)?它是 O(n),因为它需要执行一次二进制搜索并复制数组的两个部分。在最坏的情况下,复制 A 的部分将花费 O(n),而二进制搜索将花费 O(log n)。并且 O(n) + O(log n) = O(n)。
  • 哦,IC。您不是对每个子数组元素进行二进制搜索,而是使用子数组需要由原始数组中连续的元素组成的前提条件。但是 OP 指定“subA 是 A 的子集”。
  • @IvanMushketyk:是的,没有考虑过这种情况。尽管如此,如果 subA 是一个连续的子集,这可能是一个后备。 arrayCopy 几乎是一个 memmove,通常比手动复制更快。但这可能过于优化了。
【解决方案3】:

既然你说两个数组都是排序的,这听起来像是“我希望你遍历两个数组并从 subA 的成员之间删除 A 中的部分”对我来说有点像作业。

让我们试着草拟一下

  • 数组 A 按 1000 个成员排序
  • subA 按 300 个成员排序
  • arrayA 包含所有 subA 的元素

意味着我们可以做类似...

public ArrayList findDifferences(int[] arrayA, int[] subA)
{
    ArrayList retVal = new ArrayList();
    for(int i = 0; i < arrayA.size; i++)
    {  
        if(arrayA[i] < subA[index]
            retVal.add(arrayA[i]);
        else if(arrayA[i] == subA[index])
            index++;
    }
    return retVal;
}

我想说的是,你可以通过某种方式计算要复制的范围,但我猜它最终是这样的。

还有这个

 List a = new List();
 a.addAll(arrayA);
 List b = new List();
 b.addAll(subA);
 a.removeAll(b);
 return a;

【讨论】:

  • 如果 A 与 subA 共享的所有元素都位于数组 A 的开头怎么办?如果不检查索引变量是否不超过 subA 的长度,您将得到 OutOfBoundsException。
  • @IvanMushketyk 当我看到你的回答时,我知道我的已经过时了。您只是不要在集合和图表上与俄罗斯人争论 :) 这很好,先发布,她应该接受。我的只是看起来更像 java-ish :D
  • 如果arrayA多次包含一个元素,你的代码是错误的。
  • @Shark 坦率地说,我不是俄罗斯人。我是乌克兰人:)
  • @IvanMushketyk 足够接近 :) 我是塞尔维亚人。
【解决方案4】:

最快和最有效的方法是使 A \ SubA 成为 A 上的视图,即不持有对元素的自己的引用,而是由 A 和 SubA 支持。这类似于difference from Guava Sets

当然,必须考虑创建视图后对 A 和 SubA 的更改,这可能是优点或缺点,具体取决于您的情况。

任意列表的示例实现(即在您的情况下,使用new ImmutableSubarrayList&lt;E&gt;(Arrays.asList(A),Arrays.asList(SubA))

import java.util.AbstractSequentialList;
import java.util.List;
import java.util.ListIterator;
import java.util.NoSuchElementException;


public class ImmutableSubarrayList<E extends Comparable<E>> extends AbstractSequentialList<E>{

    final List<E> a, subA;
    final int size;

    public ImmutableSubarrayList(List<E> aParam, List<E> subAParam){
        super();
        a = aParam;
        subA = subAParam;
        assert a.containsAll(subA) : "second list may only contain elements from first list";

        // Iterate over a, because a.size()-subA.size() may not be correct if a contains equal elements. 
        int sizeTemp = 0;
        for (E element : a){    
            if (!subA.contains(element)){
                sizeTemp++;
            }
        }
        size = sizeTemp;
    }

    public int size() {
        return size;
    }

    public ListIterator<E> listIterator(final int firstIndex) {
        //create a ListIterator that parallely 
        // iterates over a and subA, only returning the elements in a that are not in subA
        assert (firstIndex >=0 && firstIndex <= ImmutableSubarrayList.this.size()) : "parameter was "
                           +firstIndex+" but should be betwen 0 and "+ImmutableSubarrayList.this.size();
        return new ListIterator<E>() {

            private final ListIterator<E> aIter = a.listIterator();
            private final ListIterator<E> subAIter = subA.listIterator();
            private int nextIndex = 0;

            {
                for (int lv = 0; lv < firstIndex; lv++ ){
                    next();
                }
            }

            @Override
            public boolean hasNext() {
                return nextIndex < size;
            }

            @Override
            public void add(E arg0) {
                throw new UnsupportedOperationException("The list being iteratred over is immutable");
            }

            @Override
            public boolean hasPrevious() {
                return nextIndex > 0;
            }

            @Override
            public int nextIndex() {
                return nextIndex;
            }

            @Override
            public E next() {
                if (!hasNext()){
                    throw new NoSuchElementException();
                }
                nextIndex++;
                return findNextElement();
            }

            @Override
            public E previous() {
                if (!hasPrevious()){
                    throw new NoSuchElementException();
                }
                nextIndex--;
                return findPreviousElement();
            }

            @Override
            public int previousIndex() {
                return nextIndex-1;
            }

            @Override
            public void set(E arg0) {
                throw new UnsupportedOperationException("The list being iteratred over is immutable");
            }

            @Override
            public void remove() {
                throw new UnsupportedOperationException("The list being iteratred over is immutable");          
            }

            private E findNextElement() {
                E potentialNextElement = aIter.next();
                while (subAIter.hasNext()){
                    E nextElementToBeAvoided = subAIter.next();
                    subAIter.previous();
                    assert (potentialNextElement.compareTo(nextElementToBeAvoided) > 0) : 
                        "nextElementToBeAvoided should not be smaller than potentialNextElement";
                    while (potentialNextElement.compareTo(nextElementToBeAvoided) == 0){
                        potentialNextElement = aIter.next();
                    }
                    subAIter.next();
                }
                return potentialNextElement;
            }

            //in lack of lambdas: clone of findNextElement()
            private E findPreviousElement() {
                E potentialPreviousElement = aIter.previous();
                while (subAIter.hasPrevious()){
                    E previousElementToBeAvoided = subAIter.previous();
                    subAIter.previous();
                    assert (potentialPreviousElement.compareTo(previousElementToBeAvoided) < 0) : 
                        "previousElementToBeAvoided should not be greater than potentialPreviousElement";
                    while (potentialPreviousElement.compareTo(previousElementToBeAvoided) == 0){
                        potentialPreviousElement = aIter.previous();
                    }
                    subAIter.previous();
                }
                return potentialPreviousElement;
            }
        };
    }
}

【讨论】:

  • 为什么不使用difference from Guava Sets
  • @halex 使用第三方库来区分两组?真的吗?为什么?也戴夫,没有 iterator() 方法,这是没有答案的......
  • 我不使用 Guava,纯 Java。
  • @halex 和 Sophie:好的,我已经实现了这种特殊情况,其中 subA 是 A 的子列表(对于一般列表,而不仅仅是数组)。
猜你喜欢
  • 2015-05-13
  • 1970-01-01
  • 2016-05-25
  • 2021-03-07
  • 2017-04-15
  • 2021-11-23
  • 2017-09-07
  • 2014-12-21
  • 2020-08-08
相关资源
最近更新 更多