查找堆深度比O(n ^ 2)快

时间:2019-02-28 14:02:49

标签: java algorithm heap

帮我优化算法。 我在数组中有一个堆。 数组中的每个数字都表示一个父对象。根是-1。 我需要找到堆的深度。 示例:

数组为 4 -1 4 1 1

heap_structure

答案是3。

这是我的代码

static int findMax(int[] mas) {
    int a[] = new int[mas.length];
    a[pos] = 1;
    int max = 0;

    for (int j = 0; j < mas.length; j++) {
        for (int i = 0; i < a.length; i++) {
            if (a[i] == 0 && a[mas[i]] != 0) {
                a[i] = a[mas[i]] + 1;
                if (a[i] > max)
                    max = a[i];
            }
        }
    }
    return max;
}

pos位置-根位置。

我也通过递归解决了这个问题。但是测试也给我“超过了时间限制”。

static class Node {
        static int nodesCount = 0;

        int val;
        int deep;
        List<Node> childrens = new ArrayList<>();
        static Set<Integer> deeps = new HashSet<>();

        public Node(int val, int deep) {
            this.val = val;
            this.deep = deep;
            deeps.add(deep);
            nodesCount++;
        }

        public List<Node> getChildrens() {
            return childrens;
        }

        public int getDeep() {
            return deep;
        }
    }
static int findMax(int [] mas){
    Node head = null;
    for (int i = 0; i < mas.length; i++) {
        if (mas[i] == -1)
            head = new Node(i, 1);
    }
    fillChildren(head, mas);
    return Node.deeps.stream().max(Comparator.naturalOrder()).get();
}

private static void fillChildren(Node head, int[] mas) {
    for (int i = 0; i < mas.length; i++) {
        if (mas[i] == head.val) {
            Node child = new Node(i, head.getDeep() + 1);
            head.getChildrens().add(child);
            fillChildren(child, mas);
        }
    }
}

4 个答案:

答案 0 :(得分:2)

为证实Matej的回答,这是伪代码。

  • 将D字段与每个节点相关联,

  • 将所有D初始化为-1,

  • 从每个节点开始,沿着父链,直到到达具有非负D的节点,

  • 如果到达根,则将其D设置为0,

  • 向后追溯链,不断更新D。

链遍历在遇到的第一个非负节点上停止,并且所有中间节点都变为非负。因此,负节点仅被访问一次,这证明了O(n)的行为是正确的。

更新链中的所有节点至关重要,否则可以多次访问相同的节点。在最坏的情况下,这可能导致O(n²)运算。

值得注意的是,该算法需要一个堆栈才能使向后遍历成为可能。在最坏的情况下,堆栈深度可能达到n,从而增加了O(n)额外的存储空间(或不注意堆栈溢出的风险)。

一个更好的选择可能是使用遍历节点的D字段来存储“返回索引”并形成一个临时的反向链。

答案 1 :(得分:1)

记住数组中被访问节点的深度。开始从第一个输入元素遍历到其根,然后在回溯存储深度遍历到访问的节点数组。在从子级到父级的每次跳转中,检查深度是否已计算。如果是,则不必第二次走该路线,可以直接使用预先计算的值。将是O(n)。

答案 2 :(得分:0)

我们需要的是一张地图,该地图指示孩子的位置,然后进行广度优先搜索。如下面的输出所示,复杂度为O(n)。

function f(A){
  let map = {};
  
  A.map((parentIdx, childIdx) => {
    if (map[parentIdx])
      map[parentIdx].push(childIdx);
    else
      map[parentIdx] = [childIdx];
  });
  
  let maxDepth = 0;
  let queue = [[-1, 0]];
  
  while (queue.length){
    const [node, depth] = queue.shift();

    console.log(
      `node: ${node}, children: [${map[node] || ''}], ` +
      `current depth: ${depth}`);

    maxDepth = Math.max(maxDepth, depth);
    
    if (map[node])
      for (let child of map[node])
        queue.push([child, depth + 1]);
  }
  
  return maxDepth;
}

var arr = [4, -1, 4, 1, 1];
console.log(f(arr));

答案 3 :(得分:-1)

尽管您可以根据父数组找到O(n)的最大深度,如Matej所述,但值得将该数组转换为更易于沿父级到子级方向导航的树结构节点。您已经使用Node类进行了此操作,但是您的fillChildren方法具有O(n²)复杂度,因为您必须一次又一次扫描整个数组以找到当前节点的子节点。

相反,您可以创建一个Map<Integer, Set<Integer>>,将节点映射到其子节点。这样,您可以在数组的单个循环中以O(n)的形式创建整个树。

static Map<Integer, Set<Integer>> makeTree(int[] parents) {
    Map<Integer, Set<Integer>> tree = new HashMap<>();
    for (int i = 0; i < parents.length; i++) {
        tree.computeIfAbsent(parents[i], x -> new HashSet<>()).add(i);
    }
    return tree;
}

然后,您可以轻松地编写一个递归函数以获取树的最大深度:

static int depth(Map<Integer, Set<Integer>> tree, int node) {
    return tree.containsKey(node) 
            ? 1 + tree.get(node).stream().mapToInt(n -> depth(tree, n)).max().getAsInt()
            : 0;
}

如果数组表示一棵树,则两个步骤的复杂度均为O(n),因为每个节点都被精确访问一次。如果数组还可以显示有向图,则应跟踪之前已访问过的节点。对于您的示例,使用它像这样:

int[] array = {4, -1, 4, 1, 1};
Map<Integer, Set<Integer>> tree = makeTree(array);
System.out.println(depth(tree, -1)); // 3

与任何递归算法一样,如果树非常深,则可能达到最大递归深度。可以使用Stack以迭代方式重写它,但是那时还不那么简洁。

Stack<Integer> stack = new Stack<>();
stack.add(-1);
int[] depth = new int[array.length];
while (! stack.isEmpty()) {
    int node = stack.pop();
    for (Integer child : tree.getOrDefault(node, Collections.emptySet())) {
        depth[child] = node == -1 ? 1 : depth[node] + 1;
        stack.add(child);
    }
}
System.out.println(IntStream.of(depth).max().getAsInt());