在一维数组中查找有界最近邻居

时间:2018-03-08 16:48:13

标签: algorithm performance sorting computer-science complexity-theory

我们假设我们有一些布尔值数组:

A = [0 1 1 1 1 0 0 0 0 0 0 0 1 1 1 0 0 0 1 1 0 1 1 1 1 0 0 0 ... 0]

通过对数据流执行分类来构造阵列。数组中的每个元素对应于给定小块的分类算法的输出。的数据。答案可能包括重组数组以使解析更有效。

该阵列是伪随机的,因为1&{s}和0的群组往往存在于群集中(但不一定总是如此)。

鉴于某个索引i,找到距离n最近的A[i]个零的群组的最有效方法是什么?为简单起见,请n = 1

编辑:群组应该至少 n个零。同样,对于简单的情况,这意味着至少1个零。

EDIT2:此搜索将执行o(n)次,其中n是数组的大小。 (具体来说,它是n/c,其中c是固定的持续时间。

3 个答案:

答案 0 :(得分:1)

如果你对大小为n的数组有n个查询,那么天真的方法需要花费O(n ^ 2)时间。

您可以通过合并观察到不同组大小的数量是sqrt(n)的顺序来优化这一点,因为如果我们有一个大小为1的组,大小为2,那么我们得到的最不同的组大小,大小为3之一,依此类推,我们知道1 + 2 + 3 + ... + n是n *(n + 1)/ 2,所以按照n ^ 2的顺序,但是数组的大小为n,所以不同组大小的数量是sqrt(n)的顺序。

  1. 创建一个大小为n的整数数组,表示存在多少次的组大小

  2. 为0组创建一个列表,每个元素应包含组大小和起始索引

  3. 扫描数组,将0组添加到列表中并更新当前组大小

  4. 为不同的组大小创建一个数组,每个条目应包含组大小和一个包含组起始索引的数组

  5. 创建一个整数数组或地图,通过扫描当前组大小的数组来告诉您哪个组大小在哪个索引

  6. 浏览0组列表并填写在4创建的起始索引数组。

  7. 我们最终得到一个数组,该数组占用O(n)空间,需要O(n)时间来创建并按顺序包含所有当前组大小,另外每个条目都有一个数组,其中包含该大小的组的起始索引

    要回答查询,我们可以对大于或等于给定最小组大小的所有组的起始索引进行二进制搜索。这需要O(log(n)* sqrt(n))并且我们这样做了n次,所以它总是需要O(n*log(n)*sqrt(n)) = O(n^1.5*log(n)),这比O(n ^ 2)好。

    我认为你可以通过创建一个具有所有不同组大小但不仅包含该大小的组的结构,而且还包含大于该大小的组,将其降低到O(n ^ 1.5)。这将是创建结构和回答所有n个查询的时间复杂度更快O(n * log(sqrt(n))* log(n))我认为,所以它并不重要。

    示例:

    [0 1 1 1 1 0 0 0 0 0 0 0 1 1 1 0 0, 1, 0, 0]   -- 0 indexed array
    
    hashmap = {1:[0], 2:[15, 18], 7:[5]}
    
    search(i = 7, n = 2) {
       binary search in {2:[15, 18], 7:[5]}
       return min(15, 5)
    }
    

答案 1 :(得分:1)

在此解决方案中,我组织数据,以便您可以使用二进制搜索O(log n)来查找距离最近的特定大小的组。

我首先从数组中创建零组,然后我将每组零都放入包含所有大小为s或更大的组的列表中,以便在您想要找到最近的一组s {{1或者更多,然后您只需在列表中运行二进制搜索,该列表包含大小为s或更大的所有组。

缺点在于预先处理将群组放入列表,s (我想,请检查我)时间和空间效率O(n * m)是0的组数,n是组的最大大小,但实际上效率可能更好。

以下是代码:

m

注意: public static class Group { final public int x1; final public int x2; final public int size; public Group(int x1, int x2) { assert x1 <= x2; this.x1 = x1; this.x2 = x2; this.size = x2 - x1 + 1; } public static final List<Group> getGroupsOfZeros(byte[] arr) { List<Group> listOfGroups = new ArrayList<>(); for (int i = 0; i < arr.length; i++) { if (arr[i] == 0) { int x1 = i; for (++i; i < arr.length; i++) if (arr[i] != 0) break; int x2 = i - 1; listOfGroups.add(new Group(x1, x2)); } } return Collections.unmodifiableList(listOfGroups); } public static final Group binarySearchNearest(int i, List<Group> list) { { // edge cases Group firstGroup = list.get(0); if (i <= firstGroup.x2) return firstGroup; Group lastGroup = list.get(list.size() - 1); if (i >= lastGroup.x1) return lastGroup; } int lo = 0; int hi = list.size() - 1; while (lo <= hi) { int mid = (hi + lo) / 2; Group currGroup = list.get(mid); if (i < currGroup.x1) { hi = mid - 1; } else if (i > currGroup.x2) { lo = mid + 1; } else { // x1 <= i <= x2 return currGroup; } } // intentionally swapped because: lo == hi + 1 Group lowGroup = list.get(hi); Group highGroup = list.get(lo); return (i - lowGroup.x2) < (highGroup.x1 - i) ? lowGroup : highGroup; } } 可以改进,如@maraca所描述的那样,每个不同的组大小只包含GroupsBySize个列表。我明天会更新。

Group
public static class GroupsBySize {
    private List<List<Group>> listOfGroupsBySize = new ArrayList<>();

    public GroupsBySize(List<Group> groups) {
        for (Group group : groups) {
            // ensure internal array can groups up to this size
            while (listOfGroupsBySize.size() < group.size) {
                listOfGroupsBySize.add(new ArrayList<Group>());
            }
            // add group to all lists up to its size
            for (int i = 0; i < group.size; i++) {
                listOfGroupsBySize.get(i).add(group);
            }
        }
    }

    public final Group getNearestGroupOfAtLeastSize(int index, int atLeastSize) {
        if (atLeastSize < 1)
            throw new IllegalArgumentException("group size must be greater than 0");
        List<Group> groupsOfAtLeastSize = listOfGroupsBySize.get(atLeastSize - 1);
        return Group.binarySearchNearest(index, groupsOfAtLeastSize);
    }
}

答案 2 :(得分:0)

  

找到最接近A [i]

的n个零的最有效方法是什么

如果我们不限制预处理时间和资源,最有效的方式似乎是O(1)时间和O(n * sqrt n)空间,将答案存储到所有可能的查询中。 (为了实现这一点,请运行下面的算法,列出所有可能的查询,即与每个索引配对的数组中的每个不同的零组大小。)

如果我们一次向所有n / c个查询提供,我们可以在O(n log n)时间内生成完整的结果集。

从左侧移动一次,从右侧移动一次。对于每次遍历,从我们的查询开始使用平衡二叉树,按零组大小(查询中的n)排序,其中每个节点都有一个查询索引的排序列表(所有{{1}这个特殊的i)。

在每次迭代时,当注册零组时,更新n等于和低于此零组大小的所有查询,从节点中删除所有相等和较低的索引并保留它们的记录(由于索引列表已排序,我们只删除列表的头部,当它等于或低于当前索引时),并将零组的当前索引存储在节点中(“最后看到的”零组) -指数)。如果节点中没有留下n,请将其删除。

在遍历之后,将每个节点的“上次看到的”零组索引分配给该节点中的任何剩余i。现在我们有了这次遍历的所有答案。 (在树中留下的任何查询都没有答案。)在相反的遍历中,如果有任何查询得到更好(更接近)的答案,请在最终记录中更新它。

相关问题